diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed86160b..0d4852d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,56 +1,38 @@ name: ci on: [push] jobs: - scn4me_subm: + regress: runs-on: self-hosted steps: - name: Checkout code uses: actions/checkout@v1 - - name: SCMOS test + - name: Docker build + run: | + cd ${{ github.workspace }}/docker + make build + - name: PDK Install run: | - . /home/github-runner/setup-paths.sh export OPENRAM_HOME="${{ github.workspace }}/compiler" - export OPENRAM_TECH="${{ github.workspace }}/technology:/software/PDKs/skywater-tech" - export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp" + export OPENRAM_TECH="${{ github.workspace }}/technology" + #cd $OPENRAM_HOME/tests + #export PDK_ROOT="${{ github.workspace }}/pdk" + #make pdk + #make install + - name: Regress + run: | + export OPENRAM_HOME="${{ github.workspace }}/compiler" + export OPENRAM_TECH="${{ github.workspace }}/technology" + export FREEPDK45="~/FreePDK45" + #cd $OPENRAM_HOME/.. && make pdk && make install + #export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp" #python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t scn4m_subm - $OPENRAM_HOME/tests/regress.py -j 24 -t scn4m_subm + #$OPENRAM_HOME/tests/regress.py -j 24 -t scn4m_subm + cd $OPENRAM_HOME/tests + make clean + make -k -j 48 - name: Archive if: ${{ failure() }} uses: actions/upload-artifact@v2 with: - name: scn4me_subm Archives - path: ${{ github.workspace }}/*.zip - freepdk45: - runs-on: self-hosted - steps: - - name: Checkout code - uses: actions/checkout@v1 - - name: FreePDK45 test - run: | - . /home/github-runner/setup-paths.sh - export OPENRAM_HOME="${{ github.workspace }}/compiler" - export OPENRAM_TECH="${{ github.workspace }}/technology:/software/PDKs/skywater-tech" - export OPENRAM_TMP="${{ github.workspace }}/freepdk45_temp" - #python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t freepdk45 - $OPENRAM_HOME/tests/regress.py -j 24 -t freepdk45 - - name: Archive - if: ${{ failure() }} - uses: actions/upload-artifact@v2 - with: - name: FreePDK45 Archives - path: ${{ github.workspace }}/*.zip - # coverage_stats: - # if: ${{ always() }} - # needs: [scn4me_subm, freepdk45] - # runs-on: self-hosted - # steps: - # - name: Coverage stats - # run: | - # python3-coverage combine - # python3-coverage report - # python3-coverage html -d ${{ github.workspace }}/coverage_html - # - name: Archive coverage - # uses: actions/upload-artifact@v2 - # with: - # name: code-coverage-report - # path: ${{ github.workspace }}/coverage_html/ + name: Regress Archives + path: ${{ github.workspace }}/compiler/tests/results/* diff --git a/Makefile b/Makefile index 84a9335d..214b834b 100644 --- a/Makefile +++ b/Makefile @@ -1,78 +1,111 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Regents of the University of California -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -# The top directory where environment will be created. TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) +include $(TOP_DIR)/openram.mk -.DEFAULT_GOAL := all +.DEFAULT_GOAL := install # Skywater PDK SRAM library -#SRAM_LIBRARY ?= $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_bd_sram -SRAM_GIT_REPO ?= git@github.com:google/skywater-pdk-libs-sky130_fd_bd_sram.git -SRAM_LIBRARY ?= $(TOP_DIR)/sky130_fd_bd_sram -# Open PDKs -OPEN_PDKS ?= $(PDK_ROOT)/sky130A +SRAM_LIB_DIR ?= $(PDK_ROOT)/sky130_fd_bd_sram +# Use this for release +SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git +# Use this for development +#SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git +#SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git +SRAM_LIB_GIT_COMMIT ?= a83b6468c48434d927b90058b22047843c58027b +# Open PDKs +OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks +OPEN_PDKS_GIT_REPO ?= https://github.com/RTimothyEdwards/open_pdks.git +OPEN_PDKS_GIT_COMMIT ?= 1.0.311 +#OPEN_PDKS_GIT_COMMIT ?= 7ea416610339d3c29af9d0d748ceadd3fd368608 +SKY130_PDK ?= $(PDK_ROOT)/sky130A + +# Skywater PDK +SKY130_PDKS_DIR ?= $(PDK_ROOT)/skywater-pdk +SKY130_PDKS_GIT_REPO ?= https://github.com/google/skywater-pdk.git +SKY130_PDKS_GIT_COMMIT ?= f70d8ca46961ff92719d8870a18a076370b85f6c # Create lists of all the files to copy/link -GDS_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.gds)) -MAG_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.mag)) +GDS_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.gds)) +GDS_FILES := $(GDS_FILES) $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/dlxtn/sky130_fd_sc_hd__dlxtn_1.gds +MAG_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.mag)) SPICE_SUFFIX := spice SPICE_LVS_SUFFIX := lvs.$(SPICE_SUFFIX) SPICE_CALIBRE_SUFFIX := lvs.calibre.$(SPICE_SUFFIX) +SPICE_KLAYOUT_SUFFIX := lvs.klayout.$(SPICE_SUFFIX) SPICE_BASE_SUFFIX := base.$(SPICE_SUFFIX) -ALL_SPICE_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.$(SPICE_SUFFIX))) +ALL_SPICE_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.$(SPICE_SUFFIX))) +ALL_SPICE_FILES := $(ALL_SPICE_FILES) $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/dlxtn/sky130_fd_sc_hd__dlxtn_1.spice MAGLEF_SUFFIX := maglef -MAGLEF_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.$(MAGLEF_SUFFIX))) +MAGLEF_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.$(MAGLEF_SUFFIX))) -MAGICRC_FILE := $(OPEN_PDKS)/libs.tech/magic/sky130A.magicrc +MAGICRC_FILE := $(SKY130_PDK)/libs.tech/magic/sky130A.magicrc ALL_FILES := $(ALL_SPICE_FILES) $(GDS_FILES) $(MAG_FILES) $(MAGLEF_FILES) -INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib lef_lib maglef_lib +INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib klayout_lvs_lib maglef_lib INSTALL_BASE := $(OPENRAM_HOME)/../technology/sky130 INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS)) +check-pdk-root: +ifndef PDK_ROOT + $(error PDK_ROOT is undefined, please export it before running make) +endif -install: $(INSTALL_DIRS) +$(SKY130_PDKS_DIR): check-pdk-root + @echo "Cloning skywater PDK..." + @[ -d $(PDK_ROOT)/skywater-pdk ] || \ + git clone https://github.com/google/skywater-pdk.git $(PDK_ROOT)/skywater-pdk + @cd $(SKY130_PDKS_DIR) && \ + git checkout main && git pull && \ + git checkout -qf $(SKY130_PDKS_GIT_COMMIT) && \ + git submodule update --init libraries/sky130_fd_pr/latest libraries/sky130_fd_sc_hd/latest -$(SRAM_LIBRARY): - git clone $(SRAM_GIT_REPO) $(SRAM_LIBRARY) +$(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR) + @echo "Cloning open_pdks..." + @[ -d $(OPEN_PDKS_DIR) ] || \ + git clone $(OPEN_PDKS_GIT_REPO) $(OPEN_PDKS_DIR) + @cd $(OPEN_PDKS_DIR) && git pull && git checkout $(OPEN_PDKS_GIT_COMMIT) -.PHONY: $(SRAM_LIBRARY) $(INSTALL_DIRS) install +$(SKY130_PDK): $(OPEN_PDKS_DIR) $(SKY130_PDKS_DIR) + @echo "Installing open_pdks..." + $(DOCKER_CMD) sh -c ". /home/cad-user/.bashrc && cd /pdk/open_pdks && \ + ./configure --enable-sky130-pdk=/pdk/skywater-pdk/libraries --with-sky130-local-path=/pdk && \ + cd sky130 && \ + make veryclean && \ + make && \ + make SHARED_PDKS_PATH=/pdk install" -all: $(SRAM_LIBRARY) +$(SRAM_LIB_DIR): check-pdk-root + @echo "Cloning SRAM library..." + @[ -d $(SRAM_LIB_DIR) ] || (\ + git clone $(SRAM_LIB_GIT_REPO) $(SRAM_LIB_DIR) && \ + cd $(SRAM_LIB_DIR) && git pull && git checkout $(SRAM_LIB_GIT_COMMIT)) + +install: $(SRAM_LIB_DIR) pdk + @[ -d $(PDK_ROOT)/sky130A ] || \ + (echo "Warning: $(PDK_ROOT)/sky130A not found!! Run make pdk first." && false) + @[ -d $(PDK_ROOT)/skywater-pdk ] || \ + (echo "Warning: $(PDK_ROOT)/skywater-pdk not found!! Run make pdk first." && false) @echo "Installing sky130 SRAM PDK..." @echo "PDK_ROOT='$(PDK_ROOT)'" - @echo "SRAM_LIBRARY='$(SRAM_LIBRARY)'" - @echo "OPEN_PDKS='$(OPEN_PDKS)'" - make install + @echo "SRAM_LIB_DIR='$(SRAM_LIB_DIR)'" + @echo "SKY130_PDK='$(SKY130_PDK)'" + @make $(INSTALL_DIRS) +.PHONY: install + +pdk: $(SKY130_PDK) @true +.PHONY: pdk $(INSTALL_BASE)/gds_lib: $(GDS_FILES) @echo @echo "Setting up GDS cell library for OpenRAM." @echo "==================================================================" mkdir -p $@ - @cp -va $? $@ + cp -va $? $@ @echo "==================================================================" @echo @@ -124,6 +157,18 @@ $(INSTALL_BASE)/calibre_lvs_lib: $(filter %.$(SPICE_CALIBRE_SUFFIX),$(ALL_SPICE_ @echo "==================================================================" @echo +$(INSTALL_BASE)/klayout_lvs_lib: $(filter %.$(SPICE_KLAYOUT_SUFFIX),$(ALL_SPICE_FILES)) + @echo + @echo "Setting up klayout LVS library for OpenRAM." + @echo "==================================================================" + mkdir -p $@ + @for SP in $?; do \ + cp -va $$SP $@/$$(basename $$SP .$(SPICE_KLAYOUT_SUFFIX)).sp; \ + done + @echo "==================================================================" + @echo + + $(INSTALL_BASE)/sp_lib: $(filter-out %.$(SPICE_LVS_SUFFIX) %.$(SPICE_CALIBRE_SUFFIX),$(ALL_SPICE_FILES)) @echo @echo "Setting up spice simulation library for OpenRAM." @@ -140,10 +185,33 @@ $(INSTALL_BASE)/sp_lib: $(filter-out %.$(SPICE_LVS_SUFFIX) %.$(SPICE_CALIBRE_SUF @echo "==================================================================" @echo +macros: + cd macros && make + +.PHONY: macros + clean: - rm -f $(SRAM_LIBRARY) - rm -f $(INSTALL_BASE)/tech/.magicrc - rm -f $(INSTALL_BASE)/mag_lib/.magicrc - rm -f $(INSTALL_BASE)/lef_lib/.magicrc - rm -f $(INSTALL_BASE)/maglef_lib/.magicrc - rm -rf $(INSTALL_DIRS) + @rm -f *.zip +.PHONE: clean + +uninstall: clean + @rm -f $(INSTALL_BASE)/tech/.magicrc + @rm -f $(INSTALL_BASE)/mag_lib/.magicrc + @rm -f $(INSTALL_BASE)/lef_lib/.magicrc + @rm -f $(INSTALL_BASE)/maglef_lib/.magicrc + @rm -rf $(INSTALL_DIRS) +.PHONY: uninstall + +# wipe the entire repos +wipe: uninstall + @echo $(SKY130_PDK) + @echo $(SRAM_LIB_DIR) + @echo $(OPEN_PDKS_DIR) + @echo $(SKY130_PDKS_DIR) + @echo "Wiping above PDK repos in 5 sec... (ctrl-c to quit)" + @sleep 5 + @rm -rf $(SKY130_PDK) + @rm -rf $(SRAM_LIB_DIR) + @rm -rf $(OPEN_PDKS_DIR) + @rm -rf $(SKY130_PDKS_DIR) +.PHONY: wipe diff --git a/PORTING.md b/PORTING.md new file mode 100644 index 00000000..e98871e9 --- /dev/null +++ b/PORTING.md @@ -0,0 +1,24 @@ +# Porting to a New Technology + +If you want to support a new technology, you will need to create: ++ a setup script for each technology you want to use ++ a technology directory for each technology with the base cells + +We provide two technology examples for [SCMOS] and [FreePDK45]. Each +specific technology (e.g., [FreePDK45]) should be a subdirectory +(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files: +* gds_lib folder with all the .gds (premade) library cells: + * dff.gds + * sense_amp.gds + * write_driver.gds + * cell_1rw.gds + * replica\_cell\_1rw.gds + * dummy\_cell\_1rw.gds +* sp_lib folder with all the .sp (premade) library netlists for the above cells. +* layers.map +* A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with: + * References in tech.py to spice models + * DRC/LVS rules needed for dynamic cells and routing + * Layer information + * Spice and supply information + * etc. diff --git a/README.md b/README.md index 820c52a0..438c33db 100644 --- a/README.md +++ b/README.md @@ -28,23 +28,32 @@ things that need to be fixed. ## Dependencies -The OpenRAM compiler has very few dependencies: -+ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) or [Xyce] 7.2 (or later) -+ Python 3.5 or higher +Please see the Dockerfile for the required versions of tools. + +In general, the OpenRAM compiler has very few dependencies: ++ Docker ++ Make ++ Python 3.6 or higher + Various Python packages (pip install -r requirements.txt) + [Git] -If you want to perform DRC and LVS, you will need either: -+ Calibre (for [FreePDK45]) -+ [Magic] 8.3.130 or newer -+ [Netgen] 1.5.164 or newer +## Docker + +We have a [docker setup](./docker) to run OpenRAM. To use this, you should run: +``` +cd openram/docker +make build +``` +This must be run once and will take a while to build all the tools. -You must set two environment variables: -+ OPENRAM\_HOME should point to the compiler source directory. -+ OPENERAM\_TECH should point to one or more root technology directories (colon separated). ## Environment +You must set two environment variables: ++ OPENRAM\_HOME should point to the compiler source directory. ++ OPENERAM\_TECH should point to one or more root technology directories (colon separated). + + For example add this to your .bashrc: ``` @@ -52,34 +61,44 @@ For example add this to your .bashrc: export OPENRAM_TECH="$HOME/openram/technology" ``` -You may also wish to add OPENRAM\_HOME to your PYTHONPATH: +You should also add OPENRAM\_HOME to your PYTHONPATH: ``` - export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME" + export PYTHONPATH=$OPENRAM_HOME +``` +Note that if you want symbols to resolve in your editor, you may also want to add the specific technology +directory that you use and any custom technology modules as well. For example: +``` + export PYTHONPATH="$OPENRAM_HOME:$OPENRAM_TECH/sky130:$OPENRAM_TECH/sky130/custom" ``` -We include the tech files necessary for [SCMOS] SCN4M_SUBM. The -[SCMOS] spice models, however, are generic and should be replaced with -foundry models. If you are using [FreePDK45], you should also have -that set up and have the environment variable point to the PDK. For -example add this to your .bashrc: +We include the tech files necessary for [SCMOS] SCN4M_SUBM, +[FreePDK45]. The [SCMOS] spice models, however, are +generic and should be replaced with foundry models. You may get the +entire [FreePDK45 PDK here][FreePDK45]. -``` - export FREEPDK45="/bsoe/software/design-kits/FreePDK45" -``` -You may get the entire [FreePDK45 PDK here][FreePDK45]. -If you are using [SCMOS], you should install [Magic] and [Netgen]. -We have included the most recent SCN4M_SUBM design rules from [Qflow]. +### Sky130 Setup + +To install [Sky130], you must have the open_pdks files installed in $PDK_ROOT. +To install this automatically, you can run: + + cd $HOME/openram + make pdk + +Then you must also install the [Sky130] SRAM build space and the appropriate cell views +by running: + + cd $HOME/openram + make install # Basic Usage -Once you have defined the environment, you can run OpenRAM from the command line -using a single configuration file written in Python. +Once you have defined the environment, you can run OpenRAM from the command line +using a single configuration file written in Python. For example, create a file called *myconfig.py* specifying the following parameters for your memory: - ``` # Data word size word_size = 2 @@ -116,60 +135,55 @@ python3 $OPENRAM_HOME/openram.py myconfig You can see all of the options for the configuration file in $OPENRAM\_HOME/options.py +To run designs in Docker, it is suggested to use, for example: +``` +cd openram/macros +make example_config_scn4m_subm +``` + # Unit Tests Regression testing performs a number of tests for all modules in OpenRAM. -From the unit test directory ($OPENRAM\_HOME/tests), +From the unit test directory ($OPENRAM\_HOME/tests), use the following command to run all regression tests: ``` - python3 regress.py +cd openram/compiler/tests +make -j 3 ``` -To run a specific test: +The -j can run with 3 threads. By default, this will run in all technologies. + +To run a specific test in all technologies: ``` - python3 {unit test}.py +cd openram/compiler/tests +make 05_bitcell_array_test ``` -The unit tests take the same arguments as openram.py itself. - -To increase the verbosity of the test, add one (or more) -v options: +To run a specific technology: ``` - python3 tests/00_code_format_check_test.py -v -t freepdk45 +cd openram/compiler/tests +TECHS=scn4m_subm make 05_bitcell_array_test ``` -To specify a particular technology use "-t " such as -"-t freepdk45". The default for a unit test is scn4m_subm. -The default for openram.py is specified in the configuration file. +To increase the verbosity of the test, add one (or more) -v options and +pass it as an argument to OpenRAM: +``` +ARGS="-v" make 05_bitcell_array_test +``` -# Porting to a New Technology - -If you want to support a new technology, you will need to create: -+ a setup script for each technology you want to use -+ a technology directory for each technology with the base cells - -We provide two technology examples for [SCMOS] and [FreePDK45]. Each -specific technology (e.g., [FreePDK45]) should be a subdirectory -(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files: -* gds_lib folder with all the .gds (premade) library cells: - * dff.gds - * sense_amp.gds - * write_driver.gds - * cell_1rw.gds - * replica\_cell\_1rw.gds - * dummy\_cell\_1rw.gds -* sp_lib folder with all the .sp (premade) library netlists for the above cells. -* layers.map -* A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with: - * References in tech.py to spice models - * DRC/LVS rules needed for dynamic cells and routing - * Layer information - * Spice and supply information - * etc. +Unit test results are put in a directory: +``` +openram/compiler/tests/results// +``` +If the test fails, there will be a tmp directory with intermediate results. +If the test passes, this directory will be deleted to save space. +You can view the .out file to see what the output of a test is in either case. # Get Involved ++ [Port it](./PORTING.md) to a new technology. + Report bugs by submitting [Github issues]. + Develop new features (see [how to contribute](./CONTRIBUTING.md)) -+ Submit code/fixes using a [Github pull request] ++ Submit code/fixes using a [Github pull request] + Follow our [project][Github project]. + Read and cite our [ICCAD paper][OpenRAMpaper] @@ -180,8 +194,9 @@ specific technology (e.g., [FreePDK45]) should be a subdirectory + [OpenRAM Slack Workspace][Slack] + [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe]) + [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe]) ++ @mrg@fostodon.org -# License +# License OpenRAM is licensed under the [BSD 3-clause License](./LICENSE). @@ -203,7 +218,7 @@ If I forgot to add you, please let me know! [Github issues]: https://github.com/VLSIDA/OpenRAM/issues [Github pull request]: https://github.com/VLSIDA/OpenRAM/pulls -[Github project]: https://github.com/VLSIDA/OpenRAM +[Github project]: https://github.com/VLSIDA/OpenRAM [documentation]: https://docs.google.com/presentation/d/10InGB33N51I6oBHnqpU7_w9DXlx-qe9zdrlco2Yc5co/edit?usp=sharing [dev-group]: mailto:openram-dev-group@ucsc.edu @@ -211,6 +226,7 @@ If I forgot to add you, please let me know! [dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu [user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu +[Klayout]: https://www.klayout.de/ [Magic]: http://opencircuitdesign.com/magic/ [Netgen]: http://opencircuitdesign.com/netgen/ [Qflow]: http://opencircuitdesign.com/qflow/history.html @@ -218,8 +234,10 @@ If I forgot to add you, please let me know! [Xyce]: http://xyce.sandia.gov/ [Git]: https://git-scm.com/ -[OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/ [FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents [SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf +[Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git + +[Slack]: https://join.slack.com/t/openram/shared_invite/zt-onim74ue-zlttW5XI30xvdBlJGJF6JA + -[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LWZiYWMwNjNkZThmYTdkODc3NDE1NDhjNzUxNDhmMDQ4ZTM3NDgwNWFlNjM5NWFiZDkyMzBlNzc1NTg3ZjllNTY diff --git a/compiler/Makefile b/compiler/Makefile index 171c1a10..117e4bd2 100644 --- a/compiler/Makefile +++ b/compiler/Makefile @@ -35,7 +35,7 @@ ${CELL_TESTS} \ ${MODULE_TESTS} \ ${TOP_TESTS} \ ${CHAR_TESTS} \ -${USAGE_TESTS} +${USAGE_TESTS} .PHONY: ${ALL_TESTS} @@ -64,8 +64,8 @@ usage: ${USAGE_TESTS} $(ALL_TESTS): python3 $@ -t ${TECH} - - + + OPENRAM_TECHS = $(subst :, ,$(OPENRAM_TECH)) TECH_DIR := $(word 1, $(foreach dir,$(OPENRAM_TECHS),$(wildcard $(dir)/$(TECH)))) CONFIG_DIR = $(OPENRAM_HOME)/model_configs @@ -75,9 +75,9 @@ CSV_DIR = $(TECH_DIR)/sim_data # Creates names of technology specific okay files for the configs STAMPS=$(addprefix $(SIM_DIR)/, $(addsuffix .ok, $(notdir $(basename $(MODEL_CONFIGS))))) -OPTS = +OPTS = # Characterize and perform DRC/LVS -OPTS += -c +OPTS += -c # Do not characterize or perform DRC/LVS OPTS += -n # Verbosity @@ -100,8 +100,7 @@ model: $(STAMPS) clean_model: rm -f -r $(SIM_DIR)/*.ok - + clean: find . -name \*.pyc -exec rm {} \; find . -name \*~ -exec rm {} \; - diff --git a/compiler/base/__init__.py b/compiler/base/__init__.py new file mode 100644 index 00000000..25f44cf7 --- /dev/null +++ b/compiler/base/__init__.py @@ -0,0 +1,21 @@ +from .channel_route import * +from .contact import * +from .delay_data import * +from .design import * +from .errors import * +from .geometry import * +from .hierarchy_design import * +from .hierarchy_layout import * +from .hierarchy_spice import * +from .lef import * +from .logical_effort import * +from .pin_layout import * +from .power_data import * +from .route import * +from .timing_graph import * +from .utils import * +from .vector import * +from .verilog import * +from .wire_path import * +from .wire import * +from .wire_spice_model import * diff --git a/compiler/base/channel_route.py b/compiler/base/channel_route.py index abbb267b..7cd1fb53 100644 --- a/compiler/base/channel_route.py +++ b/compiler/base/channel_route.py @@ -8,8 +8,8 @@ import collections import debug from tech import drc -from vector import vector -import design +from .vector import vector +from .design import design class channel_net(): @@ -75,7 +75,7 @@ class channel_net(): return min_overlap or max_overlap -class channel_route(design.design): +class channel_route(design): unique_id = 0 @@ -242,12 +242,12 @@ class channel_route(design.design): if self.vertical: self.add_vertical_trunk_route(net.pins, current_offset, - self.vertical_nonpref_pitch) + self.horizontal_pitch) current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch) else: self.add_horizontal_trunk_route(net.pins, current_offset, - self.horizontal_nonpref_pitch) + self.vertical_pitch) current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y) # Remove the net from other constriants in the VCG diff --git a/compiler/base/contact.py b/compiler/base/contact.py index bb6eb391..5f2f41d0 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -5,16 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import hierarchy_design import debug -from tech import drc, layer -import tech -from vector import vector -from sram_factory import factory -import sys +from .hierarchy_design import hierarchy_design +from .vector import vector +from tech import drc, layer, preferred_directions +from tech import layer as tech_layers -class contact(hierarchy_design.hierarchy_design): +class contact(hierarchy_design): """ Object for a contact shape with its conductor enclosures. Creates a contact array minimum active or poly enclosure and metal1 @@ -51,20 +49,20 @@ class contact(hierarchy_design.hierarchy_design): # Non-preferred directions if directions == "nonpref": - first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V" - second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V" + first_dir = "H" if preferred_directions[layer_stack[0]]=="V" else "V" + second_dir = "H" if preferred_directions[layer_stack[2]]=="V" else "V" self.directions = (first_dir, second_dir) # Preferred directions elif directions == "pref": - self.directions = (tech.preferred_directions[layer_stack[0]], - tech.preferred_directions[layer_stack[2]]) + self.directions = (preferred_directions[layer_stack[0]], + preferred_directions[layer_stack[2]]) # User directions elif directions: self.directions = directions # Preferred directions else: - self.directions = (tech.preferred_directions[layer_stack[0]], - tech.preferred_directions[layer_stack[2]]) + self.directions = (preferred_directions[layer_stack[0]], + preferred_directions[layer_stack[2]]) self.offset = vector(0, 0) self.implant_type = implant_type self.well_type = well_type @@ -101,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_name = second_layer # Contacts will have unique per first layer - if via_layer in tech.layer: + if via_layer in tech_layers: self.via_layer_name = via_layer elif via_layer == "contact": if first_layer in ("active", "poly"): @@ -194,7 +192,7 @@ class contact(hierarchy_design.hierarchy_design): def create_nitride_cut_enclosure(self): """ Special layer that encloses poly contacts in some processes """ # Check if there is a special poly nitride cut layer - if "npc" not in tech.layer: + if "npc" not in tech_layers: return npc_enclose_poly = drc("npc_enclose_poly") @@ -256,7 +254,7 @@ class contact(hierarchy_design.hierarchy_design): # Optionally implant well if layer exists well_layer = "{}well".format(self.well_type) - if well_layer in tech.layer: + if well_layer in tech_layers: well_width_rule = drc("minwidth_" + well_layer) self.well_enclose_active = drc(well_layer + "_enclose_active") self.well_width = max(self.first_layer_width + 2 * self.well_enclose_active, @@ -275,33 +273,3 @@ class contact(hierarchy_design.hierarchy_design): return self.return_power() -# Set up a static for each layer to be used for measurements -for layer_stack in tech.layer_stacks: - (layer1, via, layer2) = layer_stack - cont = factory.create(module_type="contact", - layer_stack=layer_stack) - module = sys.modules[__name__] - # Also create a contact that is just the first layer - if layer1 == "poly" or layer1 == "active": - setattr(module, layer1 + "_contact", cont) - else: - setattr(module, layer1 + "_via", cont) - -# Set up a static for each well contact for measurements -if "nwell" in tech.layer: - cont = factory.create(module_type="contact", - layer_stack=tech.active_stack, - implant_type="n", - well_type="n") - module = sys.modules[__name__] - setattr(module, "nwell_contact", cont) - -if "pwell" in tech.layer: - cont = factory.create(module_type="contact", - layer_stack=tech.active_stack, - implant_type="p", - well_type="p") - module = sys.modules[__name__] - setattr(module, "pwell_contact", cont) - - diff --git a/compiler/base/design.py b/compiler/base/design.py index 70a9047b..ccd9cec1 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -5,15 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from hierarchy_design import hierarchy_design -import utils -import contact +import debug from tech import GDS, layer from tech import preferred_directions from tech import cell_properties as props from globals import OPTS -import re -import debug +from . import utils +from .hierarchy_design import hierarchy_design class design(hierarchy_design): @@ -68,207 +66,20 @@ class design(hierarchy_design): self.setup_multiport_constants() + try: + from tech import power_grid + self.supply_stack = power_grid + except ImportError: + # if no power_grid is specified by tech we use sensible defaults + # Route a M3/M4 grid + self.supply_stack = self.m3_stack + def check_pins(self): for pin_name in self.pins: pins = self.get_pins(pin_name) for pin in pins: print(pin_name, pin) - @classmethod - def setup_drc_constants(design): - """ - These are some DRC constants used in many places - in the compiler. - """ - # Make some local rules for convenience - from tech import drc - for rule in drc.keys(): - # Single layer width rules - match = re.search(r"minwidth_(.*)", rule) - if match: - if match.group(1) == "active_contact": - setattr(design, "contact_width", drc(match.group(0))) - else: - setattr(design, match.group(1) + "_width", drc(match.group(0))) - - # Single layer area rules - match = re.search(r"minarea_(.*)", rule) - if match: - setattr(design, match.group(0), drc(match.group(0))) - - # Single layer spacing rules - match = re.search(r"(.*)_to_(.*)", rule) - if match and match.group(1) == match.group(2): - setattr(design, match.group(1) + "_space", drc(match.group(0))) - elif match and match.group(1) != match.group(2): - if match.group(2) == "poly_active": - setattr(design, match.group(1) + "_to_contact", - drc(match.group(0))) - else: - setattr(design, match.group(0), drc(match.group(0))) - - match = re.search(r"(.*)_enclose_(.*)", rule) - if match: - setattr(design, match.group(0), drc(match.group(0))) - - match = re.search(r"(.*)_extend_(.*)", rule) - if match: - setattr(design, match.group(0), drc(match.group(0))) - - # Create the maximum well extend active that gets used - # by cells to extend the wells for interaction with other cells - from tech import layer - design.well_extend_active = 0 - if "nwell" in layer: - design.well_extend_active = max(design.well_extend_active, design.nwell_extend_active) - if "pwell" in layer: - design.well_extend_active = max(design.well_extend_active, design.pwell_extend_active) - - # The active offset is due to the well extension - if "pwell" in layer: - design.pwell_enclose_active = drc("pwell_enclose_active") - else: - design.pwell_enclose_active = 0 - if "nwell" in layer: - design.nwell_enclose_active = drc("nwell_enclose_active") - else: - design.nwell_enclose_active = 0 - # Use the max of either so that the poly gates will align properly - design.well_enclose_active = max(design.pwell_enclose_active, - design.nwell_enclose_active, - design.active_space) - - # These are for debugging previous manual rules - if False: - print("poly_width", design.poly_width) - print("poly_space", design.poly_space) - print("m1_width", design.m1_width) - print("m1_space", design.m1_space) - print("m2_width", design.m2_width) - print("m2_space", design.m2_space) - print("m3_width", design.m3_width) - print("m3_space", design.m3_space) - print("m4_width", design.m4_width) - print("m4_space", design.m4_space) - print("active_width", design.active_width) - print("active_space", design.active_space) - print("contact_width", design.contact_width) - print("poly_to_active", design.poly_to_active) - print("poly_extend_active", design.poly_extend_active) - print("poly_to_contact", design.poly_to_contact) - print("active_contact_to_gate", design.active_contact_to_gate) - print("poly_contact_to_gate", design.poly_contact_to_gate) - print("well_enclose_active", design.well_enclose_active) - print("implant_enclose_active", design.implant_enclose_active) - print("implant_space", design.implant_space) - import sys - sys.exit(1) - - @classmethod - def setup_layer_constants(design): - """ - These are some layer constants used - in many places in the compiler. - """ - - from tech import layer_indices - import tech - for layer_id in layer_indices: - key = "{}_stack".format(layer_id) - - # Set the stack as a local helper - try: - layer_stack = getattr(tech, key) - setattr(design, key, layer_stack) - except AttributeError: - pass - - # Skip computing the pitch for non-routing layers - if layer_id in ["active", "nwell"]: - continue - - # Add the pitch - setattr(design, - "{}_pitch".format(layer_id), - design.compute_pitch(layer_id, True)) - - # Add the non-preferrd pitch (which has vias in the "wrong" way) - setattr(design, - "{}_nonpref_pitch".format(layer_id), - design.compute_pitch(layer_id, False)) - - if False: - from tech import preferred_directions - print(preferred_directions) - from tech import layer_indices - for name in layer_indices: - if name == "active": - continue - try: - print("{0} width {1} space {2}".format(name, - getattr(design, "{}_width".format(name)), - getattr(design, "{}_space".format(name)))) - - print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)), - getattr(design, "{}_nonpref_pitch".format(name)))) - except AttributeError: - pass - import sys - sys.exit(1) - - @staticmethod - def compute_pitch(layer, preferred=True): - - """ - This is the preferred direction pitch - i.e. we take the minimum or maximum contact dimension - """ - # Find the layer stacks this is used in - from tech import layer_stacks - pitches = [] - for stack in layer_stacks: - # Compute the pitch with both vias above and below (if they exist) - if stack[0] == layer: - pitches.append(design.compute_layer_pitch(stack, preferred)) - if stack[2] == layer: - pitches.append(design.compute_layer_pitch(stack[::-1], True)) - - return max(pitches) - - @staticmethod - def get_preferred_direction(layer): - return preferred_directions[layer] - - @staticmethod - def compute_layer_pitch(layer_stack, preferred): - - (layer1, via, layer2) = layer_stack - try: - if layer1 == "poly" or layer1 == "active": - contact1 = getattr(contact, layer1 + "_contact") - else: - contact1 = getattr(contact, layer1 + "_via") - except AttributeError: - contact1 = getattr(contact, layer2 + "_via") - - if preferred: - if preferred_directions[layer1] == "V": - contact_width = contact1.first_layer_width - else: - contact_width = contact1.first_layer_height - else: - if preferred_directions[layer1] == "V": - contact_width = contact1.first_layer_height - else: - contact_width = contact1.first_layer_width - layer_space = getattr(design, layer1 + "_space") - - #print(layer_stack) - #print(contact1) - pitch = contact_width + layer_space - - return utils.round_to_grid(pitch) - def setup_multiport_constants(self): """ These are contants and lists that aid multiport design. @@ -316,6 +127,4 @@ class design(hierarchy_design): total_module_power += inst.mod.analytical_power(corner, load) return total_module_power -design.setup_drc_constants() -design.setup_layer_constants() diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index d2f4e98e..3ca87e2e 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -9,13 +9,13 @@ This provides a set of useful generic types for the gdsMill interface. """ import debug -from vector import vector +from .vector import vector import tech import math import copy import numpy as np from globals import OPTS -from utils import round_to_grid +from .utils import round_to_grid class geometry: diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index fe295273..c25864f9 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -5,13 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import hierarchy_layout -import hierarchy_spice +from .hierarchy_layout import layout +from .hierarchy_spice import spice import debug +import os from globals import OPTS -class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): +class hierarchy_design(spice, layout): """ Design Class for all modules to inherit the base features. Class consisting of a set of modules and instances of these modules @@ -22,8 +23,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.drc_errors = "skipped" self.lvs_errors = "skipped" - hierarchy_spice.spice.__init__(self, name, cell_name) - hierarchy_layout.layout.__init__(self, name, cell_name) + # Flag for library cells which is recomputed in hierachy_layout + gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds" + is_library_cell = os.path.isfile(gds_file) + # Uniquify names to address the flat GDS namespace + # except for the top/output name + if not is_library_cell and name != OPTS.output_name and not name.startswith(OPTS.output_name): + name = OPTS.output_name + "_" + name + cell_name = name + + spice.__init__(self, name, cell_name) + layout.__init__(self, name, cell_name) self.init_graph_params() def get_layout_pins(self, inst): diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 8bbc72c7..fe108c01 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -5,21 +5,26 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import geometry -import gdsMill -import debug -from math import sqrt -from tech import drc, GDS -from tech import layer as techlayer -from tech import layer_indices -from tech import layer_stacks -from tech import preferred_directions import os import sys +import re +from math import sqrt +import debug +from gdsMill import gdsMill +import tech +from tech import drc, GDS +from tech import layer as tech_layer +from tech import layer_indices as tech_layer_indices +from tech import preferred_directions +from tech import layer_stacks as tech_layer_stacks +from tech import active_stack as tech_active_stack +from sram_factory import factory from globals import OPTS -from vector import vector -from pin_layout import pin_layout -from utils import round_to_grid +from .vector import vector +from .pin_layout import pin_layout +from .utils import round_to_grid +from . import geometry + try: from tech import special_purposes except ImportError: @@ -42,6 +47,7 @@ class layout(): self.cell_name = cell_name self.gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds" + self.is_library_cell = os.path.isfile(self.gds_file) self.width = None self.height = None @@ -60,38 +66,272 @@ class layout(): self.pin_map = {} # List of modules we have already visited self.visited = [] - # Flag for library cells - self.is_library_cell = False self.gds_read() + if "contact" not in self.name: + if not hasattr(layout, "_drc_constants"): + layout._drc_constants = True + layout.setup_drc_constants() + layout.setup_contacts() + layout.setup_layer_constants() + + + @classmethod + def setup_drc_constants(layout): + """ + These are some DRC constants used in many places + in the compiler. + """ + + # Make some local rules for convenience + for rule in drc.keys(): + # Single layer width rules + match = re.search(r"minwidth_(.*)", rule) + if match: + if match.group(1) == "active_contact": + setattr(layout, "contact_width", drc(match.group(0))) + else: + setattr(layout, match.group(1) + "_width", drc(match.group(0))) + + # Single layer area rules + match = re.search(r"minarea_(.*)", rule) + if match: + setattr(layout, match.group(0), drc(match.group(0))) + + # Single layer spacing rules + match = re.search(r"(.*)_to_(.*)", rule) + if match and match.group(1) == match.group(2): + setattr(layout, match.group(1) + "_space", drc(match.group(0))) + elif match and match.group(1) != match.group(2): + if match.group(2) == "poly_active": + setattr(layout, match.group(1) + "_to_contact", + drc(match.group(0))) + else: + setattr(layout, match.group(0), drc(match.group(0))) + + match = re.search(r"(.*)_enclose_(.*)", rule) + if match: + setattr(layout, match.group(0), drc(match.group(0))) + + match = re.search(r"(.*)_extend_(.*)", rule) + if match: + setattr(layout, match.group(0), drc(match.group(0))) + + # Create the maximum well extend active that gets used + # by cells to extend the wells for interaction with other cells + layout.well_extend_active = 0 + if "nwell" in tech_layer: + layout.well_extend_active = max(layout.well_extend_active, layout.nwell_extend_active) + if "pwell" in tech_layer: + layout.well_extend_active = max(layout.well_extend_active, layout.pwell_extend_active) + + # The active offset is due to the well extension + if "pwell" in tech_layer: + layout.pwell_enclose_active = drc("pwell_enclose_active") + else: + layout.pwell_enclose_active = 0 + if "nwell" in tech_layer: + layout.nwell_enclose_active = drc("nwell_enclose_active") + else: + layout.nwell_enclose_active = 0 + # Use the max of either so that the poly gates will align properly + layout.well_enclose_active = max(layout.pwell_enclose_active, + layout.nwell_enclose_active, + layout.active_space) + + # These are for debugging previous manual rules + if False: + print("poly_width", layout.poly_width) + print("poly_space", layout.poly_space) + print("m1_width", layout.m1_width) + print("m1_space", layout.m1_space) + print("m2_width", layout.m2_width) + print("m2_space", layout.m2_space) + print("m3_width", layout.m3_width) + print("m3_space", layout.m3_space) + print("m4_width", layout.m4_width) + print("m4_space", layout.m4_space) + print("active_width", layout.active_width) + print("active_space", layout.active_space) + print("contact_width", layout.contact_width) + print("poly_to_active", layout.poly_to_active) + print("poly_extend_active", layout.poly_extend_active) + print("poly_to_contact", layout.poly_to_contact) + print("active_contact_to_gate", layout.active_contact_to_gate) + print("poly_contact_to_gate", layout.poly_contact_to_gate) + print("well_enclose_active", layout.well_enclose_active) + print("implant_enclose_active", layout.implant_enclose_active) + print("implant_space", layout.implant_space) + import sys + sys.exit(1) + + @classmethod + def setup_layer_constants(layout): + """ + These are some layer constants used + in many places in the compiler. + """ try: from tech import power_grid - self.pwr_grid_layer = power_grid[0] + layout.pwr_grid_layers = [power_grid[0], power_grid[2]] except ImportError: - self.pwr_grid_layer = "m3" + layout.pwr_grid_layers = ["m3", "m4"] + + for layer_id in tech_layer_indices: + key = "{}_stack".format(layer_id) + + # Set the stack as a local helper + try: + layer_stack = getattr(tech, key) + setattr(layout, key, layer_stack) + except AttributeError: + pass + + # Skip computing the pitch for non-routing layers + if layer_id in ["active", "nwell"]: + continue + + # Add the pitch + setattr(layout, + "{}_pitch".format(layer_id), + layout.compute_pitch(layer_id, True)) + + # Add the non-preferrd pitch (which has vias in the "wrong" way) + setattr(layout, + "{}_nonpref_pitch".format(layer_id), + layout.compute_pitch(layer_id, False)) + + if False: + for name in tech_layer_indices: + if name == "active": + continue + try: + print("{0} width {1} space {2}".format(name, + getattr(layout, "{}_width".format(name)), + getattr(layout, "{}_space".format(name)))) + + print("pitch {0} nonpref {1}".format(getattr(layout, "{}_pitch".format(name)), + getattr(layout, "{}_nonpref_pitch".format(name)))) + except AttributeError: + pass + import sys + sys.exit(1) + + @staticmethod + def compute_pitch(layer, preferred=True): + """ + This is the preferred direction pitch + i.e. we take the minimum or maximum contact dimension + """ + # Find the layer stacks this is used in + pitches = [] + for stack in tech_layer_stacks: + # Compute the pitch with both vias above and below (if they exist) + if stack[0] == layer: + pitches.append(layout.compute_layer_pitch(stack, preferred)) + if stack[2] == layer: + pitches.append(layout.compute_layer_pitch(stack[::-1], True)) + + return max(pitches) + + @staticmethod + def get_preferred_direction(layer): + return preferred_directions[layer] + + @staticmethod + def compute_layer_pitch(layer_stack, preferred): + + (layer1, via, layer2) = layer_stack + try: + if layer1 == "poly" or layer1 == "active": + contact1 = getattr(layout, layer1 + "_contact") + else: + contact1 = getattr(layout, layer1 + "_via") + except AttributeError: + contact1 = getattr(layout, layer2 + "_via") + + if preferred: + if preferred_directions[layer1] == "V": + contact_width = contact1.first_layer_width + else: + contact_width = contact1.first_layer_height + else: + if preferred_directions[layer1] == "V": + contact_width = contact1.first_layer_height + else: + contact_width = contact1.first_layer_width + layer_space = getattr(layout, layer1 + "_space") + + #print(layer_stack) + #print(contact1) + pitch = contact_width + layer_space + + return round_to_grid(pitch) + + + @classmethod + def setup_contacts(layout): + # Set up a static for each layer to be used for measurements + # unless we are a contact class! + + for layer_stack in tech_layer_stacks: + (layer1, via, layer2) = layer_stack + cont = factory.create(module_type="contact", + layer_stack=layer_stack) + module = sys.modules[__name__] + # Also create a contact that is just the first layer + if layer1 == "poly" or layer1 == "active": + setattr(layout, layer1 + "_contact", cont) + else: + setattr(layout, layer1 + "_via", cont) + + # Set up a static for each well contact for measurements + if "nwell" in tech_layer: + cont = factory.create(module_type="contact", + layer_stack=tech_active_stack, + implant_type="n", + well_type="n") + module = sys.modules[__name__] + setattr(layout, "nwell_contact", cont) + + if "pwell" in tech_layer: + cont = factory.create(module_type="contact", + layer_stack=tech_active_stack, + implant_type="p", + well_type="p") + module = sys.modules[__name__] + setattr(layout, "pwell_contact", cont) + + ############################################################ # GDS layout ############################################################ - def offset_all_coordinates(self): + def offset_all_coordinates(self, offset=None): """ This function is called after everything is placed to shift the origin in the lowest left corner """ - offset = self.find_lowest_coords() - self.translate_all(offset) - return offset + if not offset: + offset = vector(0, 0) + ll = self.find_lowest_coords() + real_offset = ll + offset + self.translate_all(real_offset) + return real_offset - def offset_x_coordinates(self): + def offset_x_coordinates(self, offset=None): """ This function is called after everything is placed to shift the origin to the furthest left point. Y offset is unchanged. """ - offset = self.find_lowest_coords() - self.translate_all(offset.scale(1, 0)) - return offset + if not offset: + offset = vector(0, 0) + ll = self.find_lowest_coords() + real_offset = ll.scale(1, 0) + offset + self.translate_all(real_offset) + return real_offset def get_gate_offset(self, x_offset, height, inv_num): """ @@ -166,7 +406,7 @@ class layout(): this layout on a layer """ # Only consider the layer not the purpose for now - layerNumber = techlayer[layer][0] + layerNumber = tech_layer[layer][0] try: highestx = max(obj.rx() for obj in self.objs if obj.layerNumber == layerNumber) except ValueError: @@ -190,7 +430,7 @@ class layout(): this layout on a layer """ # Only consider the layer not the purpose for now - layerNumber = techlayer[layer][0] + layerNumber = tech_layer[layer][0] try: lowestx = min(obj.lx() for obj in self.objs if obj.layerNumber == layerNumber) except ValueError: @@ -232,6 +472,7 @@ class layout(): # Check that the instance name is unique debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name)) + self.mods.add(mod) self.inst_names.add(name) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) debug.info(3, "adding instance {}".format(self.insts[-1])) @@ -267,7 +508,7 @@ class layout(): width = drc["minwidth_{}".format(layer)] if not height: height = drc["minwidth_{}".format(layer)] - lpp = techlayer[layer] + lpp = tech_layer[layer] self.objs.append(geometry.rectangle(lpp, offset, width, @@ -283,7 +524,7 @@ class layout(): width = drc["minwidth_{}".format(layer)] if not height: height = drc["minwidth_{}".format(layer)] - lpp = techlayer[layer] + lpp = tech_layer[layer] corrected_offset = offset - vector(0.5 * width, 0.5 * height) self.objs.append(geometry.rectangle(lpp, corrected_offset, @@ -401,8 +642,8 @@ class layout(): """ pins = instance.get_pins(pin_name) - debug.check(len(pins) > 0, - "Could not find pin {}".format(pin_name)) + if len(pins) == 0: + debug.warning("Could not find pin {0} on {1}".format(pin_name, instance.mod.name)) for pin in pins: if new_name == "": @@ -413,13 +654,398 @@ class layout(): pin.width(), pin.height()) - def copy_layout_pins(self, instance, prefix=""): + + def connect_row_locs(self, from_layer, to_layer, locs, name=None, full=False): """ - Create a copied version of the layout pin at the current level. - You can optionally rename the pin to a new name. + Connects left/right rows that are aligned on the given layer. """ - for pin_name in self.pin_map.keys(): - self.copy_layout_pin(instance, pin_name, prefix + pin_name) + bins = {} + for loc in locs: + y = pin.y + try: + bins[y].append(loc) + except KeyError: + bins[y] = [loc] + + for y, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + left_x = 0 + right_x = self.width + else: + left_x = min([loc.x for loc in v]) + right_x = max([loc.x for loc in v]) + + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + + # Make sure to add vias to the new route + for loc in v: + self.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=loc, + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=to_layer, + start=left_pos, + end=right_pos) + else: + self.add_segment_center(layer=to_layer, + start=left_pos, + end=right_pos) + + def connect_row_pins(self, layer, pins, name=None, full=False): + """ + Connects left/right rows that are aligned. + """ + bins = {} + for pin in pins: + y = pin.cy() + try: + bins[y].append(pin) + except KeyError: + bins[y] = [pin] + + for y, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + left_x = 0 + right_x = self.width + else: + left_x = min([pin.lx() for pin in v]) + right_x = max([pin.rx() for pin in v]) + + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + + # Make sure to add vias to the new route + for pin in v: + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin.center(), + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=left_pos, + end=right_pos) + else: + self.add_segment_center(layer=layer, + start=left_pos, + end=right_pos) + + def connect_col_locs(self, from_layer, to_layer, locs, name=None, full=False): + """ + Connects top/bot columns that are aligned. + """ + bins = {} + for loc in locs: + x = loc.x + try: + bins[x].append(loc) + except KeyError: + bins[x] = [loc] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + bot_y = 0 + top_y = self.height + else: + bot_y = min([loc.y for loc in v]) + top_y = max([loc.y for loc in v]) + + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + + # Make sure to add vias to the new route + for loc in v: + self.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=loc, + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=to_layer, + start=top_pos, + end=bot_pos) + else: + self.add_segment_center(layer=to_layer, + start=top_pos, + end=bot_pos) + + + def connect_col_pins(self, layer, pins, name=None, full=False): + """ + Connects top/bot columns that are aligned. + """ + bins = {} + for pin in pins: + x = pin.cx() + try: + bins[x].append(pin) + except KeyError: + bins[x] = [pin] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + bot_y = 0 + top_y = self.height + else: + bot_y = min([pin.by() for pin in v]) + top_y = max([pin.uy() for pin in v]) + + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + + # Make sure to add vias to the new route + for pin in v: + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin.center(), + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=top_pos, + end=bot_pos) + else: + self.add_segment_center(layer=layer, + start=top_pos, + end=bot_pos) + + def get_metal_layers(self, from_layer, to_layer): + + from_id = tech_layer_indices[from_layer] + to_id = tech_layer_indices[to_layer] + + layer_list = [x for x in tech_layer_indices.keys() if tech_layer_indices[x] >= from_id and tech_layer_indices[x] < to_id] + + return layer_list + + + def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): + """ + Route together all of the pins of a given name that vertically align. + Uses local_insts if insts not specified. + Uses center of pin by default, or right or left if specified. + TODO: Add equally spaced option for IR drop min, right now just 2 + """ + + + bins = {} + if not insts: + insts = self.local_insts + + for inst in insts: + for pin in inst.get_pins(name): + + x = getattr(pin, xside)() + try: + bins[x].append((inst,pin)) + except KeyError: + bins[x] = [(inst,pin)] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + debug.warning("Pins don't align well so copying pins instead of connecting with pin.") + for inst,pin in v: + self.add_layout_pin(pin.name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + continue + + last_via = None + pin_layer = None + for inst,pin in v: + if layer: + pin_layer = layer + else: + pin_layer = self.supply_stack[2] + + y = getattr(pin, yside)() + + last_via = self.add_via_stack_center(from_layer=pin.layer, + to_layer=pin_layer, + offset=vector(x, y)) + + if last_via: + via_width=last_via.mod.second_layer_width + via_height=last_via.mod.second_layer_height + else: + via_width=None + via_height=0 + + bot_y = min([pin.by() for (inst,pin) in v]) + top_y = max([pin.uy() for (inst,pin) in v]) + + if full_width: + bot_y = min(0, bot_y) + top_y = max(self.height, top_y) + + top_pos = vector(x, top_y + 0.5 * via_height) + bot_pos = vector(x, bot_y - 0.5 * via_height) + +# self.add_layout_pin_rect_ends(name=name, +# layer=pin_layer, +# start=top_pos, +# end=bot_pos, +# width=via_width) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=top_pos, + end=bot_pos, + width=via_width) + + + def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): + + # This adds pins on the end connected by a segment + top_rect = self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=start) + bot_rect = self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=end) + # This is made to not overlap with the pin above + # so that the power router will only select a small pin. + # Otherwise it adds big blockages over the rails. + if start.y != end.y: + self.add_segment_center(layer=layer, + start=bot_rect.uc(), + end=top_rect.bc()) + else: + self.add_segment_center(layer=layer, + start=bot_rect.rc(), + end=top_rect.lc()) + + return (bot_rect, top_rect) + + def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): + """ + Route together all of the pins of a given name that horizontally align. + Uses local_insts if insts not specified. + Uses center of pin by default, or top or botom if specified. + TODO: Add equally spaced option for IR drop min, right now just 2 + """ + + + bins = {} + if not insts: + insts = self.local_insts + + for inst in insts: + for pin in inst.get_pins(name): + + y = getattr(pin, yside)() + try: + bins[y].append((inst,pin)) + except KeyError: + bins[y] = [(inst,pin)] + + # Filter the small bins + + for y, v in bins.items(): + if len(v) < 2: + debug.warning("Pins don't align well so copying pins instead of connecting with pin.") + for inst,pin in v: + self.add_layout_pin(pin.name, + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + continue + + last_via = None + pin_layer = None + for inst,pin in v: + if layer: + pin_layer = layer + else: + pin_layer = self.supply_stack[0] + + x = getattr(pin, xside)() + + last_via = self.add_via_stack_center(from_layer=pin.layer, + to_layer=pin_layer, + offset=vector(x, y), + min_area=True) + + if last_via: + via_height=last_via.mod.second_layer_height + via_width=last_via.mod.second_layer_width + else: + via_height=None + via_width=0 + + left_x = min([pin.lx() for (inst,pin) in v]) + right_x = max([pin.rx() for (inst,pin) in v]) + + if full_width: + left_x = min(0, left_x) + right_x = max(self.width, right_x) + + left_pos = vector(left_x + 0.5 * via_width, y) + right_pos = vector(right_x + 0.5 * via_width, y) + +# self.add_layout_pin_rect_ends(name=name, +# layer=pin_layer, +# start=left_pos, +# end=right_pos, +# width=via_height) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) + + def add_layout_end_pin_segment_center(self, text, layer, start, end): + """ + Creates a path with two pins on the end that don't overlap. + """ + + start_pin = self.add_layout_pin_rect_center(text=text, + layer=layer, + offset=start) + end_pin = self.add_layout_pin_rect_center(text=text, + layer=layer, + offset=end) + + if start.x != end.x and start.y != end.y: + file_name = "non_rectilinear.gds" + self.gds_write(file_name) + debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) + elif start.x != end.x: + self.add_segment_center(layer=layer, + start=start_pin.rc(), + end=end_pin.lc()) + elif start.y != end.y: + self.add_segment_center(layer=layer, + start=start_pin.uc(), + end=end_pin.bc()) + else: + debug.error("Cannot have a point pin.", -1) def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ @@ -449,8 +1075,8 @@ class layout(): return self.add_layout_pin(text=text, layer=layer, offset=ll_offset, - width=bbox_width, - height=bbox_height) + width=max(bbox_width, layer_width), + height=max(bbox_height, layer_width)) def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ @@ -547,22 +1173,22 @@ class layout(): def add_label(self, text, layer, offset=[0, 0], zoom=None): """Adds a text label on the given layer,offset, and zoom level""" debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset)) - lpp = techlayer[layer] + lpp = tech_layer[layer] self.objs.append(geometry.label(text, lpp, offset, zoom)) return self.objs[-1] def add_path(self, layer, coordinates, width=None): """Connects a routing path on given layer,coordinates,width.""" debug.info(4, "add path " + str(layer) + " " + str(coordinates)) - import wire_path + from . import wire_path # NOTE: (UNTESTED) add_path(...) is currently not used - # lpp = techlayer[layer] + # lpp = tech_layer[layer] # self.objs.append(geometry.path(lpp, coordinates, width)) - wire_path.wire_path(obj=self, - layer=layer, - position_list=coordinates, - width=width) + wire_path(obj=self, + layer=layer, + position_list=coordinates, + width=width) def add_route(self, layers, coordinates, layer_widths): """Connects a routing path on given layer,coordinates,width. The @@ -570,13 +1196,13 @@ class layout(): preferred direction routing whereas this includes layers in the coordinates. """ - import route + from . import route debug.info(4, "add route " + str(layers) + " " + str(coordinates)) # add an instance of our path that breaks down into rectangles and contacts - route.route(obj=self, - layer_stack=layers, - path=coordinates, - layer_widths=layer_widths) + route(obj=self, + layer_stack=layers, + path=coordinates, + layer_widths=layer_widths) def add_zjog(self, layer, start, end, first_direction="H", var_offset=0.5, fixed_offset=None): """ @@ -603,9 +1229,9 @@ class layout(): else: debug.error("Invalid direction for jog -- must be H or V.") - if layer in layer_stacks: + if layer in tech_layer_stacks: self.add_wire(layer, [start, mid1, mid2, end]) - elif layer in techlayer: + elif layer in tech_layer: self.add_path(layer, [start, mid1, mid2, end]) else: debug.error("Could not find layer {}".format(layer)) @@ -621,13 +1247,13 @@ class layout(): def add_wire(self, layers, coordinates, widen_short_wires=True): """Connects a routing path on given layer,coordinates,width. The layers are the (horizontal, via, vertical). """ - import wire + from . import wire # add an instance of our path that breaks down # into rectangles and contacts - wire.wire(obj=self, - layer_stack=layers, - position_list=coordinates, - widen_short_wires=widen_short_wires) + wire(obj=self, + layer_stack=layers, + position_list=coordinates, + widen_short_wires=widen_short_wires) def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None): """ Add a three layer via structure. """ @@ -638,7 +1264,6 @@ class layout(): directions=directions, implant_type=implant_type, well_type=well_type) - self.add_mod(via) inst = self.add_inst(name=via.name, mod=via, offset=offset) @@ -664,7 +1289,6 @@ class layout(): corrected_offset = offset + vector(-0.5 * width, -0.5 * height) - self.add_mod(via) inst = self.add_inst(name=via.name, mod=via, offset=corrected_offset) @@ -692,11 +1316,13 @@ class layout(): offset=offset) return None + intermediate_layers = self.get_metal_layers(from_layer, to_layer) + via = None cur_layer = from_layer while cur_layer != to_layer: - from_id = layer_indices[cur_layer] - to_id = layer_indices[to_layer] + from_id = tech_layer_indices[cur_layer] + to_id = tech_layer_indices[to_layer] if from_id < to_id: # grow the stack up search_id = 0 @@ -705,7 +1331,7 @@ class layout(): search_id = 2 next_id = 0 - curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) + curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, tech_layer_stacks), None) via = self.add_via_center(layers=curr_stack, size=size, @@ -714,7 +1340,9 @@ class layout(): implant_type=implant_type, well_type=well_type) - if cur_layer != from_layer or min_area: + # Only add the enclosure if we are in an intermediate layer + # or we are forced to + if min_area or cur_layer in intermediate_layers: self.add_min_area_rect_center(cur_layer, offset, via.mod.first_layer_width, @@ -733,7 +1361,6 @@ class layout(): Add a minimum area retcangle at the given point. Either width or height should be fixed. """ - min_area = drc("minarea_{}".format(layer)) if min_area == 0: return @@ -741,14 +1368,18 @@ class layout(): min_width = drc("minwidth_{}".format(layer)) if preferred_directions[layer] == "V": - height = max(min_area / width, min_width) + new_height = max(min_area / width, min_width) + new_width = width else: - width = max(min_area / height, min_width) + new_width = max(min_area / height, min_width) + new_height = height + + debug.check(min_area <= round_to_grid(new_height*new_width), "Min area violated.") self.add_rect_center(layer=layer, offset=offset, - width=width, - height=height) + width=new_width, + height=new_height) def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): """Adds a ptx module to the design.""" @@ -756,7 +1387,6 @@ class layout(): mos = ptx.ptx(width=width, mults=mults, tx_type=tx_type) - self.add_mod(mos) inst = self.add_inst(name=mos.name, mod=mos, offset=offset, @@ -768,10 +1398,6 @@ class layout(): """Reads a GDSII file in the library and checks if it exists Otherwise, start a new layout for dynamic generation.""" - # This must be done for netlist only mode too - if os.path.isfile(self.gds_file): - self.is_library_cell = True - if OPTS.netlist_only: self.gds = None return @@ -818,9 +1444,9 @@ class layout(): if not self.is_library_cell and not self.bounding_box: # If there is a boundary layer, and we didn't create one, add one. boundary_layers = [] - if "boundary" in techlayer.keys(): + if "boundary" in tech_layer.keys(): boundary_layers.append("boundary") - if "stdc" in techlayer.keys(): + if "stdc" in tech_layer.keys(): boundary_layers.append("stdc") boundary = [self.find_lowest_coords(), self.find_highest_coords()] @@ -830,7 +1456,7 @@ class layout(): width = boundary[1][0] - boundary[0][0] for boundary_layer in boundary_layers: - (layer_number, layer_purpose) = techlayer[boundary_layer] + (layer_number, layer_purpose) = tech_layer[boundary_layer] gds_layout.addBox(layerNumber=layer_number, purposeNumber=layer_purpose, offsetInMicrons=boundary[0], @@ -879,7 +1505,7 @@ class layout(): Do not write the pins since they aren't obstructions. """ if type(layer) == str: - lpp = techlayer[layer] + lpp = tech_layer[layer] else: lpp = layer @@ -1123,8 +1749,8 @@ class layout(): """ Wrapper to create a vertical channel route """ - import channel_route - cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self) + from .channel_route import channel_route + cr = channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self) # This causes problem in magic since it sometimes cannot extract connectivity of isntances # with no active devices. # self.add_inst(cr.name, cr) @@ -1135,8 +1761,8 @@ class layout(): """ Wrapper to create a horizontal channel route """ - import channel_route - cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) + from .channel_route import channel_route + cr = channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self) # This causes problem in magic since it sometimes cannot extract connectivity of isntances # with no active devices. # self.add_inst(cr.name, cr) @@ -1149,9 +1775,9 @@ class layout(): return boundary_layers = [] - if "stdc" in techlayer.keys(): + if "stdc" in tech_layer.keys(): boundary_layers.append("stdc") - if "boundary" in techlayer.keys(): + if "boundary" in tech_layer.keys(): boundary_layers.append("boundary") # Save the last one as self.bounding_box for boundary_layer in boundary_layers: @@ -1168,7 +1794,7 @@ class layout(): self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()] - def get_bbox(self, side="all", big_margin=0, little_margin=0): + def get_bbox(self, side="all", margin=0): """ Get the bounding box from the GDS """ @@ -1195,27 +1821,18 @@ class layout(): ll_offset = vector(0, 0) ur_offset = vector(0, 0) if side in ["ring", "top", "all"]: - ur_offset += vector(0, big_margin) - else: - ur_offset += vector(0, little_margin) + ur_offset += vector(0, margin) if side in ["ring", "bottom", "all"]: - ll_offset += vector(0, big_margin) - else: - ll_offset += vector(0, little_margin) + ll_offset += vector(0, margin) if side in ["ring", "left", "all"]: - ll_offset += vector(big_margin, 0) - else: - ll_offset += vector(little_margin, 0) + ll_offset += vector(margin, 0) if side in ["ring", "right", "all"]: - ur_offset += vector(big_margin, 0) - else: - ur_offset += vector(little_margin, 0) + ur_offset += vector(margin, 0) bbox = (ll - ll_offset, ur + ur_offset) size = ur - ll - debug.info(1, "Size: {0} x {1} with perimeter big margin {2} little margin {3}".format(size.x, + debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x, size.y, - big_margin, - little_margin)) + margin)) return bbox @@ -1270,7 +1887,7 @@ class layout(): for pin in pins: if new_name == "": new_name = pin.name - if pin.layer == self.pwr_grid_layer: + if pin.layer in self.pwr_grid_layers: self.add_layout_pin(new_name, pin.layer, pin.ll(), @@ -1295,22 +1912,23 @@ class layout(): def add_power_pin(self, name, loc, directions=None, start_layer="m1"): # Hack for min area if OPTS.tech_name == "sky130": - min_area = drc["minarea_{}".format(self.pwr_grid_layer)] + min_area = drc["minarea_{}".format(self.pwr_grid_layers[1])] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) else: width = None height = None - if start_layer == self.pwr_grid_layer: - self.add_layout_pin_rect_center(text=name, - layer=self.pwr_grid_layer, - offset=loc, - width=width, - height=height) + pin = None + if start_layer in self.pwr_grid_layers: + pin = self.add_layout_pin_rect_center(text=name, + layer=start_layer, + offset=loc, + width=width, + height=height) else: via = self.add_via_stack_center(from_layer=start_layer, - to_layer=self.pwr_grid_layer, + to_layer=self.pwr_grid_layers[0], offset=loc, directions=directions) @@ -1318,11 +1936,13 @@ class layout(): width = via.width if not height: height = via.height - self.add_layout_pin_rect_center(text=name, - layer=self.pwr_grid_layer, - offset=loc, - width=width, - height=height) + pin = self.add_layout_pin_rect_center(text=name, + layer=self.pwr_grid_layers[0], + offset=loc, + width=width, + height=height) + + return pin def copy_power_pin(self, pin, loc=None, directions=None, new_name=""): """ @@ -1338,22 +1958,22 @@ class layout(): # Hack for min area if OPTS.tech_name == "sky130": - min_area = drc["minarea_{}".format(self.pwr_grid_layer)] + min_area = drc["minarea_{}".format(self.pwr_grid_layers[1])] width = round_to_grid(sqrt(min_area)) height = round_to_grid(min_area / width) else: width = None height = None - if pin.layer == self.pwr_grid_layer: + if pin.layer in self.pwr_grid_layers: self.add_layout_pin_rect_center(text=new_name, - layer=self.pwr_grid_layer, + layer=pin.layer, offset=loc, width=width, height=height) else: via = self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.pwr_grid_layer, + to_layer=self.pwr_grid_layers[0], offset=loc, directions=directions) @@ -1362,7 +1982,7 @@ class layout(): if not height: height = via.height self.add_layout_pin_rect_center(text=new_name, - layer=self.pwr_grid_layer, + layer=self.pwr_grid_layers[0], offset=loc, width=width, height=height) @@ -1406,7 +2026,7 @@ class layout(): def add_dnwell(self, bbox=None, inflate=1): """ Create a dnwell, along with nwell moat at border. """ - if "dnwell" not in techlayer: + if "dnwell" not in tech_layer: return if not bbox: diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 5a10ff97..1216edc5 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -12,10 +12,11 @@ import math import tech from globals import OPTS from pprint import pformat -from delay_data import delay_data -from wire_spice_model import wire_spice_model -from power_data import power_data -import logical_effort +from .delay_data import delay_data +from .wire_spice_model import wire_spice_model +from .power_data import power_data +from .logical_effort import convert_relative_c_to_farad, convert_farad_to_relative_c + class spice(): """ @@ -36,19 +37,20 @@ class spice(): # If we have a separate lvs directory, then all the lvs files # should be in there (all or nothing!) try: - lvs_subdir = tech.lvs_lib - except AttributeError: - lvs_subdir = "lvs_lib" - lvs_dir = OPTS.openram_tech + lvs_subdir + "/" + from tech import lvs_name + lvs_dir = OPTS.openram_tech + lvs_name + "_lvs_lib/" + except ImportError: + lvs_dir = OPTS.openram_tech + "lvs_lib/" + if not os.path.exists(lvs_dir): + lvs_dir = OPTS.openram_tech + "lvs_lib/" - if os.path.exists(lvs_dir): - self.lvs_file = lvs_dir + cell_name + ".sp" - else: + self.lvs_file = lvs_dir + cell_name + ".sp" + if not os.path.exists(self.lvs_file): self.lvs_file = self.sp_file - + self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"] # Holds subckts/mods for this module - self.mods = [] + self.mods = set() # Holds the pins for this module (in order) self.pins = [] # The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND @@ -128,7 +130,7 @@ class spice(): new_list = [input_list[x] for x in self.pin_indices] return new_list - + def add_pin_types(self, type_list): """ Add pin types for all the cell's pins. @@ -140,7 +142,7 @@ class spice(): \n Module names={}\ ".format(self.name, self.pins, type_list), 1) self.pin_type = {pin: type for pin, type in zip(self.pins, type_list)} - + def get_pin_type(self, name): """ Returns the type of the signal pin. """ pin_type = self.pin_type[name] @@ -187,10 +189,6 @@ class spice(): inout_list.append(pin) return inout_list - def add_mod(self, mod): - """Adds a subckt/submodule to the subckt hierarchy""" - self.mods.append(mod) - def connect_inst(self, args, check=True): """ Connects the pins of the last instance added @@ -199,13 +197,13 @@ class spice(): where we dynamically generate groups of connections after a group of modules are generated. """ - + num_pins = len(self.insts[-1].mod.pins) num_args = len(args) # Order the arguments if the hard cell has a custom port order ordered_args = self.get_ordered_inputs(args) - + if (check and num_pins != num_args): if num_pins < num_args: mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins) @@ -281,7 +279,10 @@ class spice(): # parses line into ports and remove subckt lvs_pins = subckt_line.split(" ")[2:] debug.check(lvs_pins == self.pins, - "Spice netlists for LVS and simulation have port mismatches: {0} (LVS) vs {1} (sim)".format(lvs_pins, self.pins)) + "Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins, + self.lvs_file, + self.pins, + self.sp_file)) def check_net_in_spice(self, net_name): """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" @@ -372,15 +373,15 @@ class spice(): # these are wires and paths if self.conns[i] == []: continue - + # Instance with no devices in it needs no subckt/instance if self.insts[i].mod.no_instances: continue - + # If this is a trimmed netlist, skip it by adding comment char if trim and self.insts[i].name in self.trim_insts: sp.write("* ") - + if lvs and hasattr(self.insts[i].mod, "lvs_device"): sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name, " ".join(self.conns[i]))) @@ -423,7 +424,7 @@ class spice(): self.cacti_params = cacti_params # Get the r_on the the tx rd = self.get_on_resistance() - # Calculate the intrinsic capacitance + # Calculate the intrinsic capacitance c_intrinsic = self.get_intrinsic_capacitance() # Get wire values c_wire = self.module_wire_c() @@ -442,7 +443,7 @@ class spice(): # FIXME: Slew is not used in the model right now. # Can be added heuristically as linear factor - relative_cap = logical_effort.convert_farad_to_relative_c(load) + relative_cap = convert_farad_to_relative_c(load) stage_effort = self.get_stage_effort(relative_cap) # If it fails, then keep running with a valid object. @@ -457,13 +458,13 @@ class spice(): def module_wire_c(self): """All devices assumed to have ideal capacitance (0). - Non-ideal cases should have this function re-defined. + Non-ideal cases should have this function re-defined. """ return 0 def module_wire_r(self): """All devices assumed to have ideal resistance (0). - Non-ideal cases should have this function re-defined. + Non-ideal cases should have this function re-defined. """ return 0 @@ -510,7 +511,7 @@ class spice(): # Override this function within a module if a more accurate input capacitance is needed. # Input/outputs with differing capacitances is not implemented. relative_cap = self.input_load() - return logical_effort.convert_relative_c_to_farad(relative_cap) + return convert_relative_c_to_farad(relative_cap) def input_load(self): """Inform users undefined relative capacitance functions used for analytical delays.""" @@ -521,19 +522,19 @@ class spice(): self.cell_name)) return 0 - def cacti_rc_delay(self, + def cacti_rc_delay(self, inputramptime, # input rise time tf, # time constant of gate vs1, # threshold voltage vs2, # threshold voltage rise, # whether input rises or fall - extra_param_dict=None): + extra_param_dict=None): """By default, CACTI delay uses horowitz for gate delay. Can be overriden in cases like bitline if equation is different. """ return self.horowitz(inputramptime, tf, vs1, vs2, rise) - - def horowitz(self, + + def horowitz(self, inputramptime, # input rise time tf, # time constant of gate vs1, # threshold voltage @@ -553,17 +554,17 @@ class spice(): td = tf * math.sqrt(math.log(1.0 - vs1)*math.log(1.0 - vs1) + 2*a*b*(vs1)) + tf*(math.log(1.0 - vs1) - math.log(1.0 - vs2)) return td - - def tr_r_on(self, width, is_nchannel, stack, _is_cell): - + + def tr_r_on(self, width, is_nchannel, stack, _is_cell): + restrans = self.cacti_params["r_nch_on"] if is_nchannel else self.cacti_params["r_pch_on"] return stack * restrans / width def gate_c(self, width): - + return (tech.spice["c_g_ideal"] + tech.spice["c_overlap"] + 3*tech.spice["c_fringe"])*width +\ tech.drc["minlength_channel"]*tech.spice["cpolywire"] - + def drain_c_(self, width, stack, @@ -574,10 +575,10 @@ class spice(): c_fringe = 2*tech.spice["c_overlap"] c_overlap = 2*tech.spice["c_fringe"] drain_C_metal_connecting_folded_tr = 0 - + w_folded_tr = width/folds num_folded_tr = folds - + # Re-created some logic contact to get minwidth as importing the contact # module causes a failure if "minwidth_contact" in tech.drc: @@ -599,10 +600,10 @@ class spice(): if num_folded_tr%2 == 0: drain_h_for_sidewall = 0 - + total_drain_height_for_cap_wrt_gate *= num_folded_tr drain_C_metal_connecting_folded_tr = tech.spice["wire_c_per_um"] * total_drain_w - + drain_C_area = c_junc_area * total_drain_w * w_folded_tr drain_C_sidewall = c_junc_sidewall * (drain_h_for_sidewall + 2 * total_drain_w) diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 1d86a63e..799890e0 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug +from base import vector +from base import pin_layout from tech import layer_names import os import shutil from globals import OPTS -from vector import vector -from pin_layout import pin_layout class lef: @@ -119,14 +119,13 @@ class lef: old_blockages = list(self.blockages[pin.layer]) for blockage in old_blockages: if blockage.overlaps(inflated_pin): - intersection_shape = blockage.intersection(inflated_pin) + intersection_pin = blockage.intersection(inflated_pin) # If it is zero area, don't split the blockage - if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]: + if not intersection_pin or intersection_pin.area() == 0: continue # Remove the old blockage and add the new ones self.blockages[pin.layer].remove(blockage) - intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer) new_blockages = blockage.cut(intersection_pin) self.blockages[pin.layer].extend(new_blockages) # We split something so make another pass diff --git a/compiler/characterizer/logical_effort.py b/compiler/base/logical_effort.py similarity index 97% rename from compiler/characterizer/logical_effort.py rename to compiler/base/logical_effort.py index 20225ebe..15f2b209 100644 --- a/compiler/characterizer/logical_effort.py +++ b/compiler/base/logical_effort.py @@ -6,7 +6,7 @@ # All rights reserved. # import debug -from tech import drc, parameter, spice +from tech import parameter class logical_effort(): """ @@ -81,4 +81,4 @@ def convert_farad_to_relative_c(c_farad): def convert_relative_c_to_farad(c_relative): """Converts capacitance in logical effort relative units to Femto-Farads.""" - return c_relative/parameter['cap_relative_per_ff'] \ No newline at end of file + return c_relative/parameter['cap_relative_per_ff'] diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index cc13e049..f9b66612 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -7,7 +7,7 @@ # import debug from tech import GDS, drc -from vector import vector +from .vector import vector from tech import layer, layer_indices import math @@ -174,6 +174,10 @@ class pin_layout: def intersection(self, other): """ Check if a shape overlaps with a rectangle """ + + if not self.overlaps(other): + return None + (ll, ur) = self.rect (oll, our) = other.rect @@ -182,7 +186,10 @@ class pin_layout: min_y = max(ll.y, oll.y) max_y = min(ur.y, our.y) - return [vector(min_x, min_y), vector(max_x, max_y)] + if max_x - min_x == 0 or max_y - min_y == 0: + return None + + return pin_layout("", [vector(min_x, min_y), vector(max_x, max_y)], self.layer) def xoverlaps(self, other): """ Check if shape has x overlap """ @@ -504,12 +511,19 @@ class pin_layout: elif other.contains(self): return math.inf else: - intersections = self.compute_overlap_segment(other) + intersections = set(self.compute_overlap_segment(other)) # This is the common case where two pairs of edges overlap # at two points, so just find the distance between those two points if len(intersections) == 2: (p1, p2) = intersections return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2)) + # If we have a rectangular overlap region + elif len(intersections) == 4: + points = intersections + ll = vector(min(p.x for p in points), min(p.y for p in points)) + ur = vector(max(p.x for p in points), max(p.y for p in points)) + new_shape = pin_layout("", [ll, ur], self.lpp) + return max(new_shape.height(), new_shape.width()) else: # This is where we had a corner intersection or none return 0 diff --git a/compiler/base/route.py b/compiler/base/route.py index 812318da..2a6376e3 100644 --- a/compiler/base/route.py +++ b/compiler/base/route.py @@ -5,12 +5,12 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc import debug -from design import design +from .design import design +from .vector import vector +from .vector3d import vector3d +from tech import drc from itertools import tee -from vector import vector -from vector3d import vector3d from sram_factory import factory class route(design): diff --git a/compiler/base/utils.py b/compiler/base/utils.py index fe27c9c0..f80b23e3 100644 --- a/compiler/base/utils.py +++ b/compiler/base/utils.py @@ -8,12 +8,12 @@ import os import math -import gdsMill +from gdsMill import gdsMill import tech import globals import debug -from vector import vector -from pin_layout import pin_layout +from .vector import vector +from .pin_layout import pin_layout try: from tech import special_purposes except ImportError: diff --git a/compiler/base/vector.py b/compiler/base/vector.py index 6c4fabe2..5d011a09 100644 --- a/compiler/base/vector.py +++ b/compiler/base/vector.py @@ -51,6 +51,7 @@ class vector(): else: self.x=float(value[0]) self.y=float(value[1]) + self._hash = hash((self.x,self.y)) def __getitem__(self, index): """ @@ -104,6 +105,7 @@ class vector(): def snap_to_grid(self): self.x = self.snap_offset_to_grid(self.x) self.y = self.snap_offset_to_grid(self.y) + self._hash = hash((self.x,self.y)) return self def snap_offset_to_grid(self, offset): diff --git a/compiler/router/vector3d.py b/compiler/base/vector3d.py similarity index 100% rename from compiler/router/vector3d.py rename to compiler/base/vector3d.py diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 9cfd7d7c..414b4491 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -94,12 +94,12 @@ class verilog: self.vf.write("\n") + # This is the memory array itself + self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n\n") + for port in self.all_ports: self.register_inputs(port) - # This is the memory array itself - self.vf.write("reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n") - for port in self.all_ports: if port in self.write_ports: self.add_write_block(port) @@ -162,7 +162,7 @@ class verilog: if port in self.read_ports: self.vf.write(" #(T_HOLD) dout{0} = {1}'bx;\n".format(port, self.word_size)) if port in self.readwrite_ports: - self.vf.write(" if ( !csb{0}_reg && web{0}_reg && VERBOSE ) \n".format(port)) + self.vf.write(" if ( !csb{0}_reg && web{0}_reg && VERBOSE )\n".format(port)) self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port)) elif port in self.read_ports: self.vf.write(" if ( !csb{0}_reg && VERBOSE ) \n".format(port)) diff --git a/compiler/base/wire.py b/compiler/base/wire.py index 5c78755a..7114687c 100644 --- a/compiler/base/wire.py +++ b/compiler/base/wire.py @@ -6,8 +6,7 @@ # All rights reserved. # from tech import drc -import contact -from wire_path import wire_path +from .wire_path import wire_path from sram_factory import factory @@ -69,15 +68,24 @@ class wire(wire_path): This is contact direction independent pitch, i.e. we take the maximum contact dimension """ + + # This is here for the unit tests which may not have + # initialized the static parts of the layout class yet. + from base import layout + layout("fake", "fake") + (layer1, via, layer2) = layer_stack if layer1 == "poly" or layer1 == "active": - contact1 = getattr(contact, layer1 + "_contact") + try: + contact1 = getattr(layout, layer1 + "_contact") + except AttributeError: + breakpoint() else: try: - contact1 = getattr(contact, layer1 + "_via") + contact1 = getattr(layout, layer1 + "_via") except AttributeError: - contact1 = getattr(contact, layer2 + "_via") + contact1 = getattr(layout, layer2 + "_via") max_contact = max(contact1.width, contact1.height) layer1_space = drc("{0}_to_{0}".format(layer1)) diff --git a/compiler/base/wire_path.py b/compiler/base/wire_path.py index 411b9ede..363a41f3 100644 --- a/compiler/base/wire_path.py +++ b/compiler/base/wire_path.py @@ -5,11 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +from .vector import vector +from .utils import snap_to_grid +from .design import design from tech import drc from tech import layer as techlayer -import debug -from vector import vector -from utils import snap_to_grid def create_rectilinear_route(my_list): """ Add intermediate nodes if it isn't rectilinear. Also skip diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index b0ee56f3..b2ccd79e 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -10,7 +10,7 @@ import math import tech from globals import OPTS from sram_factory import factory -import timing_graph +from base import timing_graph class simulation(): @@ -572,7 +572,7 @@ class simulation(): self.sram.graph_exclude_column_mux(self.bitline_column, port) # Generate new graph every analysis as edges might change depending on test bit - self.graph = timing_graph.timing_graph() + self.graph = timing_graph() self.sram_instance_name = "X{}".format(self.sram.name) self.sram.build_graph(self.graph, self.sram_instance_name, self.pins) diff --git a/compiler/datasheet/__init__.py b/compiler/datasheet/__init__.py new file mode 100644 index 00000000..369ba8fe --- /dev/null +++ b/compiler/datasheet/__init__.py @@ -0,0 +1 @@ +from .datasheet_gen import datasheet_gen diff --git a/compiler/datasheet/datasheet.py b/compiler/datasheet/datasheet.py index 612a91df..d1fb3731 100644 --- a/compiler/datasheet/datasheet.py +++ b/compiler/datasheet/datasheet.py @@ -5,7 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from table_gen import * +from .table_gen import * import os import base64 from globals import OPTS diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py index 5a9e628b..f77458b4 100644 --- a/compiler/datasheet/datasheet_gen.py +++ b/compiler/datasheet/datasheet_gen.py @@ -19,8 +19,8 @@ from globals import OPTS import os import math import csv -import datasheet -import table_gen +from .datasheet import datasheet +from .table_gen import table_gen # def process_name(corner): # """ @@ -400,7 +400,7 @@ def parse_characterizer_csv(f, pages): if found == 0: # if this is the first corner for this sram, run first time configuration and set up tables - new_sheet = datasheet.datasheet(NAME) + new_sheet = datasheet(NAME) pages.append(new_sheet) new_sheet.git_id = ORIGIN_ID @@ -411,12 +411,12 @@ def parse_characterizer_csv(f, pages): new_sheet.description = [NAME, NUM_WORDS, NUM_BANKS, NUM_RW_PORTS, NUM_W_PORTS, NUM_R_PORTS, TECH_NAME, MIN_PERIOD, WORD_SIZE, ORIGIN_ID, DATETIME] - new_sheet.corners_table = table_gen.table_gen("corners") + new_sheet.corners_table = table_gen("corners") new_sheet.corners_table.add_row( ['Transistor Type', 'Power Supply', 'Temperature', 'Corner Name']) new_sheet.corners_table.add_row( [PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')]) - new_sheet.operating_table = table_gen.table_gen( + new_sheet.operating_table = table_gen( "operating_table") new_sheet.operating_table.add_row( ['Parameter', 'Min', 'Typ', 'Max', 'Units']) @@ -432,10 +432,10 @@ def parse_characterizer_csv(f, pages): # failed to provide non-zero MIN_PERIOD new_sheet.operating_table.add_row( ['Operating Frequency (F)', '', '', "not available in netlist only", 'MHz']) - new_sheet.power_table = table_gen.table_gen("power") + new_sheet.power_table = table_gen("power") new_sheet.power_table.add_row( ['Pins', 'Mode', 'Power', 'Units']) - new_sheet.timing_table = table_gen.table_gen("timing") + new_sheet.timing_table = table_gen("timing") new_sheet.timing_table.add_row( ['Parameter', 'Min', 'Max', 'Units']) # parse initial timing information @@ -592,10 +592,10 @@ def parse_characterizer_csv(f, pages): else: break - new_sheet.dlv_table = table_gen.table_gen("dlv") + new_sheet.dlv_table = table_gen("dlv") new_sheet.dlv_table.add_row(['Type', 'Description', 'Link']) - new_sheet.io_table = table_gen.table_gen("io") + new_sheet.io_table = table_gen("io") new_sheet.io_table.add_row(['Type', 'Value']) if not OPTS.netlist_only: diff --git a/compiler/datasheet/table_gen.py b/compiler/datasheet/table_gen.py index cc0d215e..99e70411 100644 --- a/compiler/datasheet/table_gen.py +++ b/compiler/datasheet/table_gen.py @@ -5,6 +5,8 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # + + class table_gen: """small library of functions to generate the html tables""" diff --git a/compiler/drc/__init__.py b/compiler/drc/__init__.py new file mode 100644 index 00000000..40e3a45c --- /dev/null +++ b/compiler/drc/__init__.py @@ -0,0 +1,6 @@ +from .custom_cell_properties import * +from .custom_layer_properties import * +from .design_rules import * +from .module_type import * +from .drc_lut import * +from .drc_value import * diff --git a/compiler/base/custom_cell_properties.py b/compiler/drc/custom_cell_properties.py similarity index 97% rename from compiler/base/custom_cell_properties.py rename to compiler/drc/custom_cell_properties.py index b2697472..0102bf24 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/drc/custom_cell_properties.py @@ -156,6 +156,16 @@ class bitcell(cell): self.storage_nets = storage_nets + self.wl_layer = "m1" + self.wl_dir = "H" + self.bl_layer = "m2" + self.bl_dir = "V" + + self.vdd_layer = "m1" + self.vdd_dir = "H" + self.gnd_layer = "m1" + self.gnd_dir = "H" + class cell_properties(): """ diff --git a/compiler/base/custom_layer_properties.py b/compiler/drc/custom_layer_properties.py similarity index 100% rename from compiler/base/custom_layer_properties.py rename to compiler/drc/custom_layer_properties.py diff --git a/compiler/drc/design_rules.py b/compiler/drc/design_rules.py index 56d0e8b6..dfa23c2a 100644 --- a/compiler/drc/design_rules.py +++ b/compiler/drc/design_rules.py @@ -6,8 +6,8 @@ # All rights reserved. # import debug -from drc_value import * -from drc_lut import * +from .drc_value import * +from .drc_lut import * class design_rules(dict): diff --git a/compiler/modules/module_type.py b/compiler/drc/module_type.py similarity index 100% rename from compiler/modules/module_type.py rename to compiler/drc/module_type.py diff --git a/compiler/example_configs/riscv_sky130_1kbyte_1rw.py b/compiler/example_configs/riscv_sky130_1kbyte_1rw.py deleted file mode 100644 index 9bdf47ed..00000000 --- a/compiler/example_configs/riscv_sky130_1kbyte_1rw.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 256 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 0 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/compiler/example_configs/riscv_sky130_1kbyte_1rw1r.py b/compiler/example_configs/riscv_sky130_1kbyte_1rw1r.py deleted file mode 100644 index d0b47857..00000000 --- a/compiler/example_configs/riscv_sky130_1kbyte_1rw1r.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 256 -write_size = 8 - -#local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -#route_supplies = False -check_lvsdrc = True -#perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/compiler/example_configs/riscv_sky130_2kbyte_1rw.py b/compiler/example_configs/riscv_sky130_2kbyte_1rw.py deleted file mode 100644 index b85df3f9..00000000 --- a/compiler/example_configs/riscv_sky130_2kbyte_1rw.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 512 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 0 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/compiler/example_configs/riscv_sky130_2kbyte_1rw1r.py b/compiler/example_configs/riscv_sky130_2kbyte_1rw1r.py deleted file mode 100644 index e94882e9..00000000 --- a/compiler/example_configs/riscv_sky130_2kbyte_1rw1r.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 512 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/compiler/example_configs/riscv_sky130_4kbyte_1rw.py b/compiler/example_configs/riscv_sky130_4kbyte_1rw.py deleted file mode 100644 index 1b6cdc07..00000000 --- a/compiler/example_configs/riscv_sky130_4kbyte_1rw.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 1024 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 0 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/compiler/example_configs/riscv_sky130_4kbyte_1rw1r.py b/compiler/example_configs/riscv_sky130_4kbyte_1rw1r.py deleted file mode 100644 index 2d53df31..00000000 --- a/compiler/example_configs/riscv_sky130_4kbyte_1rw1r.py +++ /dev/null @@ -1,25 +0,0 @@ -word_size = 32 -num_words = 1024 -write_size = 8 - -local_array_size = 16 - -num_rw_ports = 1 -num_r_ports = 1 -num_w_ports = 0 - -tech_name = "sky130" -nominal_corner_only = True - -route_supplies = False -check_lvsdrc = True -perimeter_pins = False -#netlist_only = True -#analytical_delay = False -output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, - num_r_ports, - num_w_ports, - word_size, - num_words, - tech_name) -output_path = "macro/{}".format(output_name) diff --git a/compiler/globals.py b/compiler/globals.py index 32c3abc6..4ff6332c 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -22,7 +22,7 @@ import getpass import subprocess -VERSION = "1.1.19" +VERSION = "1.2.0" NAME = "OpenRAM v{}".format(VERSION) USAGE = "openram.py [options] \nUse -h for help.\n" @@ -252,7 +252,8 @@ def setup_bitcell(): # See if bitcell exists try: - __import__(OPTS.bitcell) + c = importlib.import_module("modules." + OPTS.bitcell) + mod = getattr(c, OPTS.bitcell) except ImportError: # Use the pbitcell if we couldn't find a custom bitcell # or its custom replica bitcell @@ -377,7 +378,6 @@ def read_config(config_file, is_unit_test=True): ports, OPTS.tech_name) - def end_openram(): """ Clean up openram for a proper exit """ cleanup_paths() @@ -431,19 +431,12 @@ def setup_paths(): OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) except: debug.error("$OPENRAM_HOME is not properly defined.", 1) + debug.check(os.path.isdir(OPENRAM_HOME), "$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME)) - # Add all of the subdirs to the python path - # These subdirs are modules and don't need - # to be added: characterizer, verify - subdirlist = [item for item in os.listdir(OPENRAM_HOME) if os.path.isdir(os.path.join(OPENRAM_HOME, item))] - for subdir in subdirlist: - full_path = "{0}/{1}".format(OPENRAM_HOME, subdir) - debug.check(os.path.isdir(full_path), - "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path)) - if "__pycache__" not in full_path: - sys.path.append("{0}".format(full_path)) + if OPENRAM_HOME not in sys.path: + debug.error("Please add OPENRAM_HOME to the PYTHONPATH.", -1) # Use a unique temp subdirectory if multithreaded if OPTS.num_threads > 1 or OPTS.openram_temp == "/tmp": @@ -570,18 +563,18 @@ def import_tech(): OPTS.openram_tech = os.path.dirname(tech_mod.__file__) + "/" - # Add the tech directory + # Prepend the tech directory so it is sourced FIRST tech_path = OPTS.openram_tech - sys.path.append(tech_path) + sys.path.insert(0, tech_path) try: import tech except ImportError: debug.error("Could not load tech module.", -1) - # Add custom modules of the technology to the path, if they exist + # Prepend custom modules of the technology to the path, if they exist custom_mod_path = os.path.join(tech_path, "modules/") if os.path.exists(custom_mod_path): - sys.path.append(custom_mod_path) + sys.path.insert(0, custom_mod_path) def print_time(name, now_time, last_time=None, indentation=2): diff --git a/compiler/modules/__init__.py b/compiler/modules/__init__.py new file mode 100644 index 00000000..b2f78ba7 --- /dev/null +++ b/compiler/modules/__init__.py @@ -0,0 +1,82 @@ +from .and2_dec import * +from .and3_dec import * +from .and4_dec import * +from .bank import * +from .bitcell_1port import * +from .bitcell_2port import * +from .bitcell_array import * +from .bitcell_base_array import * +from .bitcell_base import * +from .col_cap_array import * +from .col_cap_bitcell_1port import * +from .col_cap_bitcell_2port import * +from .column_decoder import * +from .column_mux_array import * +from .column_mux import * +from .control_logic import * +from .delay_chain import * +from .dff_array import * +from .dff_buf_array import * +from .dff_buf import * +from .dff_inv_array import * +from .dff_inv import * +from .dff import * +from .dummy_array import * +from .dummy_bitcell_1port import * +from .dummy_bitcell_2port import * +from .dummy_pbitcell import * +from .global_bitcell_array import * +from .hierarchical_decoder import * +from .hierarchical_predecode2x4 import * +from .hierarchical_predecode3x8 import * +from .hierarchical_predecode4x16 import * +from .hierarchical_predecode import * +from .inv_dec import * +from .local_bitcell_array import * +from .nand2_dec import * +from .nand3_dec import * +from .nand4_dec import * +from .orig_bitcell_array import * +from .pand2 import * +from .pand3 import * +from .pand4 import * +from .pbitcell import * +from .pbuf_dec import * +from .pbuf import * +from .pdriver import * +from .pgate import * +from .pinvbuf import * +from .pinv_dec import * +from .pinv import * +from .pnand2 import * +from .pnand3 import * +from .pnand4 import * +from .pnor2 import * +from .port_address import * +from .port_data import * +from .precharge_array import * +from .precharge import * +from .ptristate_inv import * +from .ptx import * +from .pwrite_driver import * +from .replica_bitcell_1port import * +from .replica_bitcell_2port import * +from .replica_bitcell_array import * +from .replica_column import * +from .replica_pbitcell import * +from .row_cap_array import * +from .row_cap_bitcell_1port import * +from .row_cap_bitcell_2port import * +from .sense_amp_array import * +from .sense_amp import * +from .tri_gate_array import * +from .tri_gate import * +from .wordline_buffer_array import * +from .wordline_driver_array import * +from .wordline_driver import * +from .write_driver_array import * +from .write_driver import * +from .write_mask_and_array import * +from .sram_1bank import * +from .sram_config import * +from .sram import * diff --git a/compiler/modules/and2_dec.py b/compiler/modules/and2_dec.py index d448e3ff..8ce10caa 100644 --- a/compiler/modules/and2_dec.py +++ b/compiler/modules/and2_dec.py @@ -6,20 +6,20 @@ # All rights reserved. # import debug -from vector import vector -import design +from base import vector +from base import design from sram_factory import factory from globals import OPTS from tech import layer -class and2_dec(design.design): +class and2_dec(design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - design.design.__init__(self, name) + design.__init__(self, name) debug.info(1, "Creating and2_dec {}".format(name)) self.add_comment("size: {}".format(size)) @@ -43,9 +43,6 @@ class and2_dec(design.design): height=self.height, size=self.size) - self.add_mod(self.nand) - self.add_mod(self.inv) - def create_layout(self): if "li" in layer: diff --git a/compiler/modules/and3_dec.py b/compiler/modules/and3_dec.py index 410b2379..9c8ee348 100644 --- a/compiler/modules/and3_dec.py +++ b/compiler/modules/and3_dec.py @@ -6,19 +6,19 @@ # All rights reserved. # import debug -from vector import vector -import design +from base import design +from base import vector from sram_factory import factory from globals import OPTS from tech import layer -class and3_dec(design.design): +class and3_dec(design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - design.design.__init__(self, name) + design.__init__(self, name) debug.info(1, "Creating and3_dec {}".format(name)) self.add_comment("size: {}".format(size)) self.size = size @@ -41,9 +41,6 @@ class and3_dec(design.design): height=self.height, size=self.size) - self.add_mod(self.nand) - self.add_mod(self.inv) - def create_layout(self): if "li" in layer: self.route_layer = "li" diff --git a/compiler/modules/and4_dec.py b/compiler/modules/and4_dec.py index 99026bab..6d75eedd 100644 --- a/compiler/modules/and4_dec.py +++ b/compiler/modules/and4_dec.py @@ -6,20 +6,20 @@ # All rights reserved. # import debug -from vector import vector -import design +from base import design +from base import vector from sram_factory import factory from globals import OPTS from tech import layer -class and4_dec(design.design): +class and4_dec(design): """ This is an AND with configurable drive strength. """ def __init__(self, name, size=1, height=None, add_wells=True): - design.design.__init__(self, name) + design.__init__(self, name) debug.info(1, "Creating and4_dec {}".format(name)) self.add_comment("size: {}".format(size)) @@ -43,9 +43,6 @@ class and4_dec(design.design): height=self.height, size=self.size) - self.add_mod(self.nand) - self.add_mod(self.inv) - def create_layout(self): if "li" in layer: self.route_layer = "li" @@ -129,4 +126,3 @@ class and4_dec(design.design): offset=pin.center(), width=pin.width(), height=pin.height()) - diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 7a36abf6..88d8abc7 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -6,16 +6,16 @@ # All rights reserved. # import debug -import design +from base import design +from base import vector from sram_factory import factory from math import log, ceil, floor from tech import drc -from vector import vector from globals import OPTS from tech import layer_properties as layer_props -class bank(design.design): +class bank(design): """ Dynamically generated a single bank including bitcell array, hierarchical_decoder, precharge, (optional column_mux and column decoder), @@ -229,7 +229,7 @@ class bank(design.design): # UPPER LEFT QUADRANT # To the left of the bitcell array above the predecoders and control logic - x_offset = self.m2_gap + self.port_address[port].width + x_offset = self.decoder_gap + self.port_address[port].width self.port_address_offsets[port] = vector(-x_offset, self.main_bitcell_array_bottom) @@ -272,7 +272,7 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the right of the bitcell array - x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap + x_offset = self.bitcell_array_right + self.port_address[port].width + self.decoder_gap self.port_address_offsets[port] = vector(x_offset, self.main_bitcell_array_bottom) @@ -364,9 +364,9 @@ class bank(design.design): self.num_col_addr_lines = 0 self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines - # A space for wells or jogging m2 - self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), - 3 * self.m2_pitch, + # Gap between decoder and array + self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), + 2 * self.m2_pitch, drc("nwell_to_nwell")) def add_modules(self): @@ -389,7 +389,6 @@ class bank(design.design): self.bitcell_array = factory.create(module_type="replica_bitcell_array", cols=self.num_cols + self.num_spare_cols, rows=self.num_rows) - self.add_mod(self.bitcell_array) self.port_address = [] for port in self.all_ports: @@ -397,21 +396,17 @@ class bank(design.design): cols=self.num_cols + self.num_spare_cols, rows=self.num_rows, port=port)) - self.add_mod(self.port_address[port]) self.port_data = [] self.bit_offsets = self.get_column_offsets() for port in self.all_ports: - temp_pre = factory.create(module_type="port_data", - sram_config=self.sram_config, - port=port, - bit_offsets=self.bit_offsets) - self.port_data.append(temp_pre) - self.add_mod(self.port_data[port]) + self.port_data.append(factory.create(module_type="port_data", + sram_config=self.sram_config, + port=port, + bit_offsets=self.bit_offsets)) if(self.num_banks > 1): self.bank_select = factory.create(module_type="bank_select") - self.add_mod(self.bank_select) def create_bitcell_array(self): """ Creating Bitcell Array """ @@ -528,26 +523,9 @@ class bank(design.design): if self.col_addr_size == 0: return - elif self.col_addr_size == 1: - self.column_decoder = factory.create(module_type="pinvbuf", - height=self.dff.height) - elif self.col_addr_size == 2: - self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", - column_decoder=True, - height=self.dff.height) - - elif self.col_addr_size == 3: - self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", - column_decoder=True, - height=self.dff.height) - elif self.col_addr_size == 4: - self.column_decoder = factory.create(module_type="hierarchical_predecode4x16", - column_decoder=True, - height=self.dff.height) else: - # No error checking before? - debug.error("Invalid column decoder?", -1) - self.add_mod(self.column_decoder) + self.column_decoder = factory.create(module_type="column_decoder", + col_addr_size=self.col_addr_size) self.column_decoder_inst = [None] * len(self.all_ports) for port in self.all_ports: @@ -611,15 +589,22 @@ class bank(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ + # Copy only the power pins already on the power layer # (this won't add vias to internal bitcell pins, for example) - for inst in self.insts: - self.copy_power_pins(inst, "vdd", add_vias=False) - self.copy_power_pins(inst, "gnd", add_vias=False) + + # This avoids getting copy errors on vias and other instances + all_insts = [self.bitcell_array_inst] + self.port_address_inst + self.port_data_inst + if hasattr(self, "column_decoder_inst"): + all_insts += self.column_decoder_inst + + for inst in all_insts: + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins: for pin_name, supply_name in zip(['vnb','vpb'],['gnd','vdd']): - self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name) + self.copy_layout_pin(self.bitcell_array_inst, pin_name, new_name=supply_name) # If we use the pinvbuf as the decoder, we need to add power pins. # Other decoders already have them. @@ -926,23 +911,14 @@ class bank(design.design): stack = getattr(self, layer_props.bank.stack) pitch = getattr(self, layer_props.bank.pitch) - if self.col_addr_size == 1: + decode_names = [] + for i in range(self.num_col_addr_lines): + decode_names.append("out_{}".format(i)) - # Connect to sel[0] and sel[1] - decode_names = ["Zb", "Z"] - - # The Address LSB - self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port)) - - elif self.col_addr_size > 1: - decode_names = [] - for i in range(self.num_col_addr_lines): - decode_names.append("out_{}".format(i)) - - for i in range(self.col_addr_size): - decoder_name = "in_{}".format(i) - addr_name = "addr{0}_{1}".format(port, i) - self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) + for i in range(self.col_addr_size): + decoder_name = "in_{}".format(i) + addr_name = "addr{0}_{1}".format(port, i) + self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name) if port % 2: offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0) diff --git a/compiler/modules/bank_select.py b/compiler/modules/bank_select.py index dcaec9d7..4d299c6e 100644 --- a/compiler/modules/bank_select.py +++ b/compiler/modules/bank_select.py @@ -5,19 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import sys -from tech import drc, parameter -import debug -import design -import contact -from pinv import pinv -from pnand2 import pnand2 -from pnor2 import pnor2 -from vector import vector +from tech import drc +from base import design +from base import vector +from pgates import pinv +from pgates import pnand2 +from pgates import pnor2 from sram_factory import factory from globals import OPTS -class bank_select(design.design): +class bank_select(design): """Create a bank select signal that is combined with an array of NOR+INV gates to gate the control signals in case of multiple banks are created in upper level SRAM module @@ -78,20 +75,15 @@ class bank_select(design.design): # 1x Inverter self.inv_sel = factory.create(module_type="pinv", height=height) - self.add_mod(self.inv_sel) # 4x Inverter self.inv4x = factory.create(module_type="pinv", height=height, size=4) - self.add_mod(self.inv4x) self.nor2 = factory.create(module_type="pnor2", height=height) - self.add_mod(self.nor2) self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4) - self.add_mod(self.inv4x_nor) self.nand2 = factory.create(module_type="pnand2", height=height) - self.add_mod(self.nand2) def calculate_module_offsets(self): diff --git a/compiler/bitcells/bitcell_1port.py b/compiler/modules/bitcell_1port.py similarity index 93% rename from compiler/bitcells/bitcell_1port.py rename to compiler/modules/bitcell_1port.py index 12c3c3ce..ed8bc8f3 100644 --- a/compiler/bitcells/bitcell_1port.py +++ b/compiler/modules/bitcell_1port.py @@ -7,10 +7,10 @@ # import debug from tech import cell_properties as props -import bitcell_base +from .bitcell_base import bitcell_base -class bitcell_1port(bitcell_base.bitcell_base): +class bitcell_1port(bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It is a hand-made cell, so diff --git a/compiler/bitcells/bitcell_2port.py b/compiler/modules/bitcell_2port.py similarity index 97% rename from compiler/bitcells/bitcell_2port.py rename to compiler/modules/bitcell_2port.py index 75d654c0..86eddf7e 100644 --- a/compiler/bitcells/bitcell_2port.py +++ b/compiler/modules/bitcell_2port.py @@ -7,10 +7,10 @@ # import debug from tech import cell_properties as props -import bitcell_base +from .bitcell_base import bitcell_base -class bitcell_2port(bitcell_base.bitcell_base): +class bitcell_2port(bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It is a hand-made cell, so @@ -103,4 +103,4 @@ class bitcell_2port(bitcell_base.bitcell_base): def is_non_inverting(self): """Return input to output polarity for module""" - return False \ No newline at end of file + return False diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 238d499f..e50c5b73 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -6,7 +6,7 @@ # All rights reserved. # import debug -from bitcell_base_array import bitcell_base_array +from .bitcell_base_array import bitcell_base_array from tech import drc, spice from globals import OPTS from sram_factory import factory @@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() @@ -53,7 +55,6 @@ class bitcell_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ self.cell = factory.create(module_type=OPTS.bitcell) - self.add_mod(self.cell) def create_instances(self): """ Create the module instances used in this design """ @@ -64,11 +65,11 @@ class bitcell_array(bitcell_base_array): self.cell_inst[row, col]=self.add_inst(name=name, mod=self.cell) self.connect_inst(self.get_bitcell_pins(row, col)) - + # If it is a "core" cell, it could be trimmed for sim time if col>0 and col0 and row 1: + for i in range(self.num_inputs): + self.copy_layout_pin(self.column_decoder_inst, "in_{0}".format(i)) + + for i in range(self.num_outputs): + self.copy_layout_pin(self.column_decoder_inst, "out_{0}".format(i)) + + def route_layout(self): + """ Create routing among the modules """ + self.route_layout_pins() + self.route_supplies() + + def route_supplies(self): + """ Propagate all vdd/gnd pins up to this level for all modules """ + if self.col_addr_size == 1: + self.copy_layout_pin(self.column_decoder_inst, "vdd") + self.copy_layout_pin(self.column_decoder_inst, "gnd") + else: + self.route_vertical_pins("vdd", self.insts, xside="rx",) + self.route_vertical_pins("gnd", self.insts, xside="lx",) + + def add_modules(self): + + self.dff =factory.create(module_type="dff") + + if self.col_addr_size == 1: + self.column_decoder = factory.create(module_type="pinvbuf", + height=self.dff.height) + elif self.col_addr_size == 2: + self.column_decoder = factory.create(module_type="hierarchical_predecode2x4", + column_decoder=True, + height=self.dff.height) + + elif self.col_addr_size == 3: + self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", + column_decoder=True, + height=self.dff.height) + elif self.col_addr_size == 4: + self.column_decoder = factory.create(module_type="hierarchical_predecode4x16", + column_decoder=True, + height=self.dff.height) + else: + # No error checking before? + debug.error("Invalid column decoder?", -1) + diff --git a/compiler/pgates/column_mux.py b/compiler/modules/column_mux.py similarity index 97% rename from compiler/pgates/column_mux.py rename to compiler/modules/column_mux.py index 0dd923ba..67c44894 100644 --- a/compiler/pgates/column_mux.py +++ b/compiler/modules/column_mux.py @@ -5,16 +5,17 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import pgate +from .pgate import * import debug from tech import drc, layer -from vector import vector +from base import vector +from .pgate import * from sram_factory import factory from tech import cell_properties as cell_props from globals import OPTS -class column_mux(pgate.pgate): +class column_mux(pgate): """ This module implements the columnmux bitline cell used in the design. Creates a single column mux cell with the given integer size relative @@ -77,7 +78,6 @@ class column_mux(pgate.pgate): self.ptx_width = self.tx_size * drc("minwidth_tx") self.nmos = factory.create(module_type="ptx", width=self.ptx_width) - self.add_mod(self.nmos) # Space it in the center self.nmos_lower = self.add_inst(name="mux_tx1", @@ -241,10 +241,9 @@ class column_mux(pgate.pgate): self.add_via_center(layers=self.col_mux_stack, offset=active_pos) - # Add the M1->..->power_grid_layer stack - self.add_power_pin(name="gnd", - loc=active_pos, - start_layer="m1") + self.add_layout_pin_rect_center(text="gnd", + layer="m1", + offset=active_pos) # Add well enclosure over all the tx and contact if "pwell" in layer: diff --git a/compiler/modules/column_mux_array.py b/compiler/modules/column_mux_array.py index 544b37e1..fa264d37 100644 --- a/compiler/modules/column_mux_array.py +++ b/compiler/modules/column_mux_array.py @@ -5,16 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design import debug from tech import layer, preferred_directions -from vector import vector +from base import vector from sram_factory import factory from globals import OPTS from tech import layer_properties as layer_props -class column_mux_array(design.design): +class column_mux_array(design): """ Dynamically generated column mux array. Array of column mux to read the bitlines through the 6T. @@ -87,7 +87,6 @@ class column_mux_array(design.design): self.mux = factory.create(module_type="column_mux", bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) - self.add_mod(self.mux) self.cell = factory.create(module_type=OPTS.bitcell) @@ -148,13 +147,14 @@ class column_mux_array(design.design): offset=offset, height=self.height - offset.y) - for inst in self.mux_inst: - self.copy_layout_pin(inst, "gnd") + def route_supplies(self): + self.route_horizontal_pins("gnd", self.insts) def add_routing(self): self.add_horizontal_input_rail() self.add_vertical_poly_rail() self.route_bitlines() + self.route_supplies() def add_horizontal_input_rail(self): """ Create address input rails below the mux transistors """ @@ -174,16 +174,17 @@ class column_mux_array(design.design): sel_index = col % self.words_per_row # Add the column x offset to find the right select bit gate_offset = self.mux_inst[col].get_pin("sel").bc() - # height to connect the gate to the correct horizontal row - # sel_height = self.get_pin("sel_{}".format(sel_index)).by() # use the y offset from the sel pin and the x offset from the gate + offset = vector(gate_offset.x, self.get_pin("sel_{}".format(sel_index)).cy()) + + bl_offset = offset.scale(0, 1) + vector((self.mux_inst[col].get_pin("br_out").cx() + self.mux_inst[col].get_pin("bl_out").cx())/2, 0) self.add_via_stack_center(from_layer="poly", to_layer=self.sel_layer, - offset=offset, + offset=bl_offset, directions=self.via_directions) - self.add_path("poly", [offset, gate_offset]) + self.add_path("poly", [offset, gate_offset, bl_offset]) def route_bitlines(self): """ Connect the output bit-lines to form the appropriate width mux """ diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 9565958a..e7c1d345 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -5,16 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design import debug from sram_factory import factory import math -from vector import vector +from base import vector from globals import OPTS -import logical_effort +from base import logical_effort -class control_logic(design.design): +class control_logic(design): """ Dynamically generated Control logic for the total SRAM circuit. """ @@ -43,7 +43,7 @@ class control_logic(design.design): self.num_words = num_rows * words_per_row self.enable_delay_chain_resizing = False - self.inv_parasitic_delay = logical_effort.logical_effort.pinv + self.inv_parasitic_delay = logical_effort.pinv # Determines how much larger the sen delay should be. Accounts for possible error in model. # FIXME: This should be made a parameter @@ -91,17 +91,13 @@ class control_logic(design.design): rows=self.num_control_signals, columns=1) - self.add_mod(self.ctrl_dff_array) - self.and2 = factory.create(module_type="pand2", size=12, height=dff_height) - self.add_mod(self.and2) self.rbl_driver = factory.create(module_type="pbuf", size=self.num_cols, height=dff_height) - self.add_mod(self.rbl_driver) # clk_buf drives a flop for every address addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2) @@ -114,8 +110,6 @@ class control_logic(design.design): fanout=clock_fanout, height=dff_height) - self.add_mod(self.clk_buf_driver) - # We will use the maximum since this same value is used to size the wl_en # and the p_en_bar drivers # max_fanout = max(self.num_rows, self.num_cols) @@ -126,25 +120,21 @@ class control_logic(design.design): self.wl_en_driver = factory.create(module_type="pdriver", size_list=size_list, height=dff_height) - self.add_mod(self.wl_en_driver) # w_en drives every write driver self.wen_and = factory.create(module_type="pand3", - size=self.word_size + 8, - height=dff_height) - self.add_mod(self.wen_and) + size=self.word_size + 8, + height=dff_height) # s_en drives every sense amp self.sen_and3 = factory.create(module_type="pand3", size=self.word_size + self.num_spare_cols, height=dff_height) - self.add_mod(self.sen_and3) # used to generate inverted signals with low fanout self.inv = factory.create(module_type="pinv", size=1, height=dff_height) - self.add_mod(self.inv) # p_en_bar drives every column in the bitcell array # but it is sized the same as the wl_en driver with @@ -152,17 +142,14 @@ class control_logic(design.design): self.p_en_bar_driver = factory.create(module_type="pdriver", fanout=self.num_cols, height=dff_height) - self.add_mod(self.p_en_bar_driver) self.nand2 = factory.create(module_type="pnand2", height=dff_height) - self.add_mod(self.nand2) debug.check(OPTS.delay_chain_stages % 2, "Must use odd number of delay chain stages for inverting delay chain.") self.delay_chain=factory.create(module_type="delay_chain", fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ]) - self.add_mod(self.delay_chain) def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" @@ -293,7 +280,8 @@ class control_logic(design.design): def route_rails(self): """ Add the input signal inverted tracks """ height = self.control_logic_center.y - self.m2_pitch - offset = vector(self.ctrl_dff_array.width, 0) + # DFF spacing plus the power routing + offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0) self.input_bus = self.create_vertical_bus("m2", offset, @@ -325,7 +313,8 @@ class control_logic(design.design): self.place_dffs() # All of the control logic is placed to the right of the DFFs and bus - self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + # as well as the power supply stripe + self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch row = 0 # Add the logic on the right of the bus @@ -381,7 +370,7 @@ class control_logic(design.design): self.route_clk_buf() self.route_gated_clk_bar() self.route_gated_clk_buf() - self.route_supply() + self.route_supplies() def create_delay(self): """ Create the replica bitline """ @@ -720,28 +709,74 @@ class control_logic(design.design): start=out_pos, end=right_pos) - def route_supply(self): + def route_supplies(self): """ Add vdd and gnd to the instance cells """ - supply_layer = self.dff.get_pin("vdd").layer + pin_layer = self.dff.get_pin("vdd").layer + supply_layer = self.supply_stack[2] + + # FIXME: We should be able to replace this with route_vertical_pins instead + # but we may have to make the logic gates a separate module so that they + # have row pins of the same width max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) + min_row_x_loc = self.control_x_offset + + vdd_pin_locs = [] + gnd_pin_locs = [] + + last_via = None for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: - if pin.layer == supply_layer: + if pin.layer == pin_layer: row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("vdd", pin_loc, start_layer=pin.layer) - self.add_path(supply_layer, [row_loc, pin_loc]) + vdd_pin_locs.append(pin_loc) + last_via = self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) + self.add_path(pin_layer, [row_loc, pin_loc]) pins = inst.get_pins("gnd") for pin in pins: - if pin.layer == supply_layer: + if pin.layer == pin_layer: row_loc = pin.rc() - pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("gnd", pin_loc, start_layer=pin.layer) - self.add_path(supply_layer, [row_loc, pin_loc]) + pin_loc = vector(min_row_x_loc, pin.rc().y) + gnd_pin_locs.append(pin_loc) + last_via = self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) + self.add_path(pin_layer, [row_loc, pin_loc]) + + if last_via: + via_height=last_via.mod.second_layer_height + via_width=last_via.mod.second_layer_width + else: + via_height=None + via_width=0 + + min_y = min([x.y for x in vdd_pin_locs]) + max_y = max([x.y for x in vdd_pin_locs]) + bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height) + top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height) + self.add_layout_pin_segment_center(text="vdd", + layer=supply_layer, + start=bot_pos, + end=top_pos, + width=via_width) + + min_y = min([x.y for x in gnd_pin_locs]) + max_y = max([x.y for x in gnd_pin_locs]) + bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height) + top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height) + self.add_layout_pin_segment_center(text="gnd", + layer=supply_layer, + start=bot_pos, + end=top_pos, + width=via_width) self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "vdd") @@ -794,7 +829,7 @@ class control_logic(design.design): # Connect this at the bottom of the buffer out_pin = inst.get_pin("Z") out_pos = out_pin.center() - mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height) + mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height) mid2 = vector(self.input_bus[name].cx(), mid1.y) bus_pos = self.input_bus[name].center() self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index 7e4830c9..c393e280 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -6,13 +6,13 @@ # All rights reserved. # import debug -import design -from vector import vector +from base import design +from base import vector from globals import OPTS from sram_factory import factory -class delay_chain(design.design): +class delay_chain(design): """ Generate a delay chain with the given number of stages and fanout. Input is a list contains the electrical effort (fanout) of each stage. @@ -47,7 +47,7 @@ class delay_chain(design.design): self.height = self.rows * self.inv.height # The width is determined by the largest fanout plus the driver self.width = (max(self.fanout_list) + 1) * self.inv.width - + self.place_inverters() self.route_inverters() self.route_supplies() @@ -66,10 +66,9 @@ class delay_chain(design.design): self.dff = factory.create(module_type="dff_buf") dff_height = self.dff.height - + self.inv = factory.create(module_type="pinv", height=dff_height) - self.add_mod(self.inv) def create_inverters(self): """ Create the inverters and connect them based on the stage list """ @@ -173,20 +172,11 @@ class delay_chain(design.design): self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) def route_supplies(self): - # Add power and ground to all the cells except: - # the fanout driver, the right-most load - # The routing to connect the loads is over the first and last cells - # We have an even number of drivers and must only do every other - # supply rail - - for inst in self.driver_inst_list: - load_list = self.load_inst_map[inst] - for pin_name in ["vdd", "gnd"]: - pin = load_list[0].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) - - pin = load_list[-2].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) + # These pins get routed in one cell from the left/right + # because the input signal gets routed on M3 and can interfere with the delay input. + self.route_vertical_pins("vdd", self.driver_inst_list, xside="rx") + right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] + self.route_vertical_pins("gnd", right_load_insts, xside="lx") def add_layout_pins(self): @@ -199,7 +189,7 @@ class delay_chain(design.design): to_layer="m2", offset=mid_loc) self.add_path(a_pin.layer, [a_pin.center(), mid_loc]) - + self.add_layout_pin_rect_center(text="in", layer="m2", offset=mid_loc) @@ -213,4 +203,3 @@ class delay_chain(design.design): self.add_layout_pin_rect_center(text="out", layer="m1", offset=a_pin.center()) - diff --git a/compiler/custom/dff.py b/compiler/modules/dff.py similarity index 96% rename from compiler/custom/dff.py rename to compiler/modules/dff.py index 33bd2b3a..0f7cb777 100644 --- a/compiler/custom/dff.py +++ b/compiler/modules/dff.py @@ -5,12 +5,12 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design from tech import cell_properties as props from tech import spice -class dff(design.design): +class dff(design): """ Memory address flip-flop """ diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 951c2f36..5a9070c7 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -6,13 +6,13 @@ # All rights reserved. # import debug -import design -from vector import vector +from base import design +from base import vector from sram_factory import factory from globals import OPTS -class dff_array(design.design): +class dff_array(design): """ This is a simple row (or multiple rows) of flops. Unlike the data flops, these are never spaced out. @@ -42,13 +42,13 @@ class dff_array(design.design): self.height = self.rows * self.dff.height self.place_dff_array() + self.route_supplies() self.add_layout_pins() self.add_boundary() self.DRC_LVS() def add_modules(self): self.dff = factory.create(module_type="dff") - self.add_mod(self.dff) def add_pins(self): for row in range(self.rows): @@ -107,17 +107,26 @@ class dff_array(design.design): return dout_name + def route_supplies(self): + if self.rows > 1: + # Vertical straps on ends if multiple rows + left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] + right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] + self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") + self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") + else: + + # Add connections every 4 cells + for col in range(0, self.columns, 4): + vdd_pin=self.dff_insts[0, col].get_pin("vdd") + self.add_power_pin("vdd", vdd_pin.lc(), start_layer=vdd_pin.layer) + + # Add connections every 4 cells + for col in range(0, self.columns, 4): + gnd_pin=self.dff_insts[0, col].get_pin("gnd") + self.add_power_pin("gnd", gnd_pin.rc(), start_layer=gnd_pin.layer) + def add_layout_pins(self): - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin) - - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin) - for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row, col].get_pin("D") diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index d979c0fe..b0cd619c 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -6,14 +6,14 @@ # All rights reserved. # import debug -import design +from base import design from tech import layer -from vector import vector +from base import vector from globals import OPTS from sram_factory import factory -class dff_buf(design.design): +class dff_buf(design): """ This is a simple buffered DFF. The output is buffered with two inverters, of variable size, to provide q @@ -58,17 +58,14 @@ class dff_buf(design.design): def add_modules(self): self.dff = factory.create(module_type="dff") - self.add_mod(self.dff) self.inv1 = factory.create(module_type="pinv", size=self.inv1_size, height=self.dff.height) - self.add_mod(self.inv1) self.inv2 = factory.create(module_type="pinv", size=self.inv2_size, height=self.dff.height) - self.add_mod(self.inv2) def add_pins(self): self.add_pin_list(["D", "Q", "Qb", "clk", "vdd", "gnd"], @@ -110,7 +107,7 @@ class dff_buf(design.design): pass well_spacing += 2 * self.well_extend_active - + self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing, 0)) # Add INV2 to the right diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 31d55872..e284b589 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -6,13 +6,13 @@ # All rights reserved. # import debug -import design -from vector import vector +from base import design +from base import vector from globals import OPTS from sram_factory import factory -class dff_buf_array(design.design): +class dff_buf_array(design): """ This is a simple row (or multiple rows) of flops. Unlike the data flops, these are never spaced out. @@ -68,7 +68,6 @@ class dff_buf_array(design.design): self.dff = factory.create(module_type="dff_buf", inv1_size=self.inv1_size, inv2_size=self.inv2_size) - self.add_mod(self.dff) def create_dff_array(self): self.dff_insts={} @@ -155,15 +154,23 @@ class dff_buf_array(design.design): gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin, loc=vdd_pin.lc()) + if self.rows > 1: + # Vertical straps on ends if multiple rows + left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] + right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] + self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") + self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") + else: + for row in range(self.rows): + for col in range(self.columns): + # Continous vdd rail along with label. + vdd_pin=self.dff_insts[row, col].get_pin("vdd") + self.copy_power_pin(vdd_pin) + + # Continous gnd rail along with label. + gnd_pin=self.dff_insts[row, col].get_pin("gnd") + self.copy_power_pin(gnd_pin) - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin, loc=gnd_pin.lc()) def add_layout_pins(self): diff --git a/compiler/modules/dff_inv.py b/compiler/modules/dff_inv.py index 958a8d40..4d162c23 100644 --- a/compiler/modules/dff_inv.py +++ b/compiler/modules/dff_inv.py @@ -6,14 +6,13 @@ # All rights reserved. # import debug -import design -from tech import drc -from math import log -from vector import vector +from base import design +from base import vector from globals import OPTS -from pinv import pinv +from sram_factory import factory -class dff_inv(design.design): + +class dff_inv(design): """ This is a simple DFF with an inverted output. Some DFFs do not have Qbar, so this will create it. @@ -66,12 +65,10 @@ class dff_inv(design.design): def add_modules(self): self.dff = dff_inv.dff_inv(self.inv_size) - self.add_mod(self.dff) self.inv1 = factory.create(module_type="pinv", size=self.inv_size, height=self.dff.height) - self.add_mod(self.inv1) def create_modules(self): self.dff_inst=self.add_inst(name="dff_inv_dff", diff --git a/compiler/modules/dff_inv_array.py b/compiler/modules/dff_inv_array.py index 2f81d9a8..97cbd590 100644 --- a/compiler/modules/dff_inv_array.py +++ b/compiler/modules/dff_inv_array.py @@ -6,13 +6,13 @@ # All rights reserved. # import debug -import design -from vector import vector +from base import design +from base import vector from globals import OPTS from sram_factory import factory -class dff_inv_array(design.design): +class dff_inv_array(design): """ This is a simple row (or multiple rows) of flops. Unlike the data flops, these are never spaced out. @@ -53,7 +53,6 @@ class dff_inv_array(design.design): def add_modules(self): self.dff = factory.create(module_type="dff") - self.add_mod(self.dff) def add_pins(self): for row in range(self.rows): @@ -129,11 +128,11 @@ class dff_inv_array(design.design): for col in range(self.columns): # Adds power pin on left of row vdd_pin=self.dff_insts[row,col].get_pin("vdd") - self.add_power_pin(vdd_pin, loc=vdd_pin.lc()) + self.add_power_pin(vdd_pin, loc=vdd_pin.lc(), start_layer=vdd_pin.layer) # Adds gnd pin on left of row gnd_pin=self.dff_insts[row,col].get_pin("gnd") - self.add_power_pin(gnd_pin, loc=gnd_pin.lc()) + self.add_power_pin(gnd_pin, loc=gnd_pin.lc(), start_layer=gnd_pin.layer) for row in range(self.rows): for col in range(self.columns): diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 51aece55..566122e7 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -3,7 +3,7 @@ # Copyright (c) 2016-2021 Regents of the University of California # All rights reserved. # -from bitcell_base_array import bitcell_base_array +from .bitcell_base_array import bitcell_base_array from sram_factory import factory from globals import OPTS @@ -36,6 +36,8 @@ class dummy_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() @@ -45,7 +47,6 @@ class dummy_array(bitcell_base_array): self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell) self.cell = factory.create(module_type=OPTS.bitcell) - self.add_mod(self.dummy_cell) def create_instances(self): """ Create the module instances used in this design """ @@ -99,6 +100,8 @@ class dummy_array(bitcell_base_array): width=self.width, height=wl_pin.height()) + def route_supplies(self): + # Copy a vdd/gnd layout pin from every cell for row in range(self.row_size): for col in range(self.column_size): diff --git a/compiler/bitcells/dummy_bitcell_1port.py b/compiler/modules/dummy_bitcell_1port.py similarity index 89% rename from compiler/bitcells/dummy_bitcell_1port.py rename to compiler/modules/dummy_bitcell_1port.py index 6573394e..3c6f9da6 100644 --- a/compiler/bitcells/dummy_bitcell_1port.py +++ b/compiler/modules/dummy_bitcell_1port.py @@ -7,10 +7,10 @@ # import debug from tech import cell_properties as props -import bitcell_base +from .bitcell_base import bitcell_base -class dummy_bitcell_1port(bitcell_base.bitcell_base): +class dummy_bitcell_1port(bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It is a hand-made cell, so diff --git a/compiler/bitcells/dummy_bitcell_2port.py b/compiler/modules/dummy_bitcell_2port.py similarity index 89% rename from compiler/bitcells/dummy_bitcell_2port.py rename to compiler/modules/dummy_bitcell_2port.py index a1a96810..94f99f39 100644 --- a/compiler/bitcells/dummy_bitcell_2port.py +++ b/compiler/modules/dummy_bitcell_2port.py @@ -7,10 +7,10 @@ # import debug from tech import cell_properties as props -import bitcell_base +from .bitcell_base import bitcell_base -class dummy_bitcell_2port(bitcell_base.bitcell_base): +class dummy_bitcell_2port(bitcell_base): """ A single bit cell which is forced to store a 0. This module implements the single memory cell used in the design. It diff --git a/compiler/bitcells/dummy_pbitcell.py b/compiler/modules/dummy_pbitcell.py similarity index 95% rename from compiler/bitcells/dummy_pbitcell.py rename to compiler/modules/dummy_pbitcell.py index c2082033..7b099218 100644 --- a/compiler/bitcells/dummy_pbitcell.py +++ b/compiler/modules/dummy_pbitcell.py @@ -6,13 +6,13 @@ # All rights reserved. # import debug -import design -from vector import vector +from base import design +from base import vector from globals import OPTS from sram_factory import factory -class dummy_pbitcell(design.design): +class dummy_pbitcell(design): """ Creates a replica bitcell using pbitcell """ @@ -23,7 +23,7 @@ class dummy_pbitcell(design.design): self.num_r_ports = OPTS.num_r_ports self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports - design.design.__init__(self, name) + design.__init__(self, name) debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports, self.num_w_ports, self.num_r_ports)) @@ -56,7 +56,6 @@ class dummy_pbitcell(design.design): def add_modules(self): self.prbc = factory.create(module_type="pbitcell", dummy_bitcell=True) - self.add_mod(self.prbc) self.height = self.prbc.height self.width = self.prbc.width diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py index bd1c243b..b25c37de 100644 --- a/compiler/modules/global_bitcell_array.py +++ b/compiler/modules/global_bitcell_array.py @@ -5,16 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import bitcell_base_array +from .bitcell_base_array import bitcell_base_array from globals import OPTS from sram_factory import factory -from vector import vector +from base import vector import debug from numpy import cumsum from tech import layer_properties as layer_props -class global_bitcell_array(bitcell_base_array.bitcell_base_array): +class global_bitcell_array(bitcell_base_array): """ Creates a global bitcell array. Rows is an integer number for all local arrays. @@ -64,7 +64,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): rbl=self.rbl, left_rbl=[0], right_rbl=[1] if len(self.all_ports) > 1 else []) - self.add_mod(la) self.local_mods.append(la) return @@ -90,7 +89,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): cols=cols, rbl=self.rbl) - self.add_mod(la) self.local_mods.append(la) def add_pins(self): @@ -344,4 +342,3 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array): """Exclude dffs from graph as they do not represent critical path""" self.graph_inst_exclude.add(self.ctrl_dff_inst) - diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 24267719..52b71229 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -6,17 +6,17 @@ # All rights reserved. # import debug -import design +from base import design import math from sram_factory import factory -from vector import vector +from base import vector from globals import OPTS from tech import layer_indices from tech import layer_stacks from tech import layer_properties as layer_props from tech import drc -class hierarchical_decoder(design.design): +class hierarchical_decoder(design): """ Dynamically generated hierarchical decoder. """ @@ -57,11 +57,12 @@ class hierarchical_decoder(design.design): self.route_inputs() self.route_outputs() self.route_decoder_bus() - self.route_vdd_gnd() self.offset_x_coordinates() - self.width = self.and_inst[0].rx() + 0.5 * self.m1_width + self.width = self.and_inst[0].rx() + + self.route_supplies() self.add_boundary() self.DRC_LVS() @@ -69,14 +70,11 @@ class hierarchical_decoder(design.design): def add_modules(self): self.and2 = factory.create(module_type="and2_dec", height=self.cell_height) - self.add_mod(self.and2) self.and3 = factory.create(module_type="and3_dec", height=self.cell_height) - self.add_mod(self.and3) # TBD # self.and4 = factory.create(module_type="and4_dec") - # self.add_mod(self.and4) self.add_decoders() @@ -84,15 +82,12 @@ class hierarchical_decoder(design.design): """ Create the decoders based on the number of pre-decodes """ self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4", height=self.cell_height) - self.add_mod(self.pre2_4) self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8", height=self.cell_height) - self.add_mod(self.pre3_8) self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16", height=self.cell_height) - self.add_mod(self.pre4_16) def determine_predecodes(self, num_inputs): """ @@ -194,7 +189,7 @@ class hierarchical_decoder(design.design): self.output_layer_pitch = getattr(self, self.output_layer + "_pitch") # Two extra pitches between modules on left and right - self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch + self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + 2 * self.bus_pitch self.row_decoder_height = self.and2.height * self.num_outputs # Extra bus space for supply contacts @@ -501,11 +496,11 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): # This leaves an offset for the predecoder output jogs input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer, - pitch=self.bus_pitch, - offset=vector(self.bus_pitch, 0), - names=input_bus_names, - length=self.height) + self.predecode_bus = self.create_vertical_bus(layer=self.bus_layer, + pitch=self.bus_pitch, + offset=vector(self.bus_pitch, 0), + names=input_bus_names, + length=self.height) self.route_bus_to_decoder() self.route_predecodes_to_bus() @@ -593,44 +588,20 @@ class hierarchical_decoder(design.design): output_index) output_index = output_index + 1 - def route_vdd_gnd(self): + def route_supplies(self): """ - Add a pin for each row of vdd/gnd which are - must-connects next level up. """ - if layer_props.hierarchical_decoder.vertical_supply: - for n in ["vdd", "gnd"]: - pins = self.and_inst[0].get_pins(n) - for pin in pins: - self.add_rect(layer=pin.layer, - offset=pin.ll() + vector(0, self.bus_space), - width=pin.width(), - height=self.height - 2 * self.bus_space) + # Leave these to route in the port_address + all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst + for inst in all_insts: + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") + + self.route_vertical_pins("vdd", self.and_inst, xside="rx",) + self.route_vertical_pins("gnd", self.and_inst, xside="lx",) - # This adds power vias at the top of each cell - # (except the last to keep them inside the boundary) - for i in self.and_inst[:-1]: - pins = i.get_pins(n) - for pin in pins: - self.copy_power_pin(pin, loc=pin.uc()) - for i in self.pre2x4_inst + self.pre3x8_inst: - self.copy_layout_pin(i, n) - else: - # The vias will be placed at the right of the cells. - xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space - for row in range(0, self.num_outputs): - for pin_name in ["vdd", "gnd"]: - # The nand and inv are the same height rows... - supply_pin = self.and_inst[row].get_pin(pin_name) - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) - - # Copy the pins from the predecoders - for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst: - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(pre, pin_name) def route_predecode_bus_outputs(self, rail_name, pin, row): """ @@ -698,7 +669,7 @@ class hierarchical_decoder(design.design): drc_error = 0 for and_input in self.predecode_bus_rail_pos: if and_input.x == rail_pos.x: - if (abs(y_offset - and_input.y) < total_buffer_space) or (abs(y_offset - and_input.y) < via.height): + if (abs(y_offset - and_input.y) < total_buffer_space) or (abs(y_offset - and_input.y) < via.height) or (abs(y_offset - drc("minwidth_{}".format(cur_layer)) - pin_pos.y - via.height/2) < drc("{0}_to_{0}".format(cur_layer)) ): drc_error = 1 if drc_error == 0: break diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 30908c4c..867b113b 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -6,9 +6,9 @@ # All rights reserved. # import debug -import design +from base import design import math -from vector import vector +from base import vector from sram_factory import factory from globals import OPTS from tech import layer_properties as layer_props @@ -18,7 +18,7 @@ from tech import preferred_directions from tech import drc -class hierarchical_predecode(design.design): +class hierarchical_predecode(design): """ Pre 2x4 and 3x8 and TBD 4x16 decoder shared code. """ @@ -31,7 +31,7 @@ class hierarchical_predecode(design.design): self.cell_height = b.height else: self.cell_height = height - + self.column_decoder = column_decoder self.input_and_rail_pos = [] self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) @@ -59,13 +59,11 @@ class hierarchical_predecode(design.design): inv_type = "inv_dec" self.and_mod = factory.create(module_type=and_type, height=self.cell_height) - self.add_mod(self.and_mod) # This uses the pinv_dec parameterized cell self.inv = factory.create(module_type=inv_type, height=self.cell_height, size=1) - self.add_mod(self.inv) def create_layout(self): """ The general organization is from left to right: @@ -189,13 +187,13 @@ class hierarchical_predecode(design.design): self.route_input_inverters() self.route_input_ands() self.route_output_inverters() - self.route_inputs_to_rails() + self.route_inputs_to_rails() self.route_output_ands() - self.route_vdd_gnd() + self.route_supplies() def route_inputs_to_rails(self): """ Route the uninverted inputs to the second set of rails """ - + top_and_gate = self.and_inst[-1] for num in range(self.number_of_inputs): if num == 0: @@ -221,7 +219,7 @@ class hierarchical_predecode(design.design): to_layer=self.bus_layer, offset=[self.input_rails[in_pin].cx(), y_offset], directions= ("H", "H")) - + self.add_via_stack_center(from_layer=self.input_layer, to_layer=self.bus_layer, offset=[self.decode_rails[a_pin].cx(), y_offset], @@ -306,7 +304,7 @@ class hierarchical_predecode(design.design): else: # grow the stack down search_id = 2 next_id = 0 - + curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None) via = factory.create(module_type="contact", @@ -343,7 +341,7 @@ class hierarchical_predecode(design.design): """ Route the different permutations of the NAND/AND decocer cells. """ - + # This 2D array defines the connection mapping and_input_line_combination = self.get_and_input_line_combination() for k in range(self.number_of_outputs): @@ -380,10 +378,10 @@ class hierarchical_predecode(design.design): offset=pin_pos, directions=direction) - def route_vdd_gnd(self): + def route_supplies(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # We may ahve vertical power supply rails + # We may have vertical power supply rails if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder: for n in ["vdd", "gnd"]: # This makes a wire from top to bottom for both inv and and gates @@ -397,7 +395,7 @@ class hierarchical_predecode(design.design): height=top_pin.uy() - self.bus_pitch) # This adds power vias at the top of each cell # (except the last to keep them inside the boundary) - for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]: + for i in [self.inv_inst[0], self.inv_inst[-2], self.and_inst[0], self.and_inst[-2]]: pins = i.get_pins(n) for pin in pins: self.copy_power_pin(pin, loc=pin.uc()) @@ -405,8 +403,6 @@ class hierarchical_predecode(design.design): # In other techs, we are using standard cell decoder cells with horizontal power else: for num in range(0, self.number_of_outputs): - - # Route both supplies for n in ["vdd", "gnd"]: and_pins = self.and_inst[num].get_pins(n) for and_pin in and_pins: @@ -415,10 +411,9 @@ class hierarchical_predecode(design.design): end=vector(self.width, and_pin.cy())) # Add pins in two locations - for xoffset in [self.inv_inst[0].lx() - self.bus_space, - self.and_inst[0].lx() - self.bus_space]: - pin_pos = vector(xoffset, and_pin.cy()) - self.copy_power_pin(and_pin, loc=pin_pos) - - - + if n == "vdd": + xoffset = self.and_inst[0].lx() - self.bus_space + else: + xoffset = self.inv_inst[0].lx() - self.bus_space + pin_pos = vector(xoffset, and_pin.cy()) + self.add_power_pin(n, pin_pos, start_layer=and_pin.layer) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index 941a0756..bdd01499 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -5,7 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from hierarchical_predecode import hierarchical_predecode +from .hierarchical_predecode import hierarchical_predecode from globals import OPTS diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index ef70a282..dc8e026c 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -5,7 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from hierarchical_predecode import hierarchical_predecode +from .hierarchical_predecode import hierarchical_predecode from globals import OPTS diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py index 64eef96d..7227bf3b 100644 --- a/compiler/modules/hierarchical_predecode4x16.py +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -5,7 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from hierarchical_predecode import hierarchical_predecode +from .hierarchical_predecode import hierarchical_predecode from globals import OPTS diff --git a/compiler/custom/inv_dec.py b/compiler/modules/inv_dec.py similarity index 82% rename from compiler/custom/inv_dec.py rename to compiler/modules/inv_dec.py index d963783f..8f143e29 100644 --- a/compiler/custom/inv_dec.py +++ b/compiler/modules/inv_dec.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design -import logical_effort +from base import design +from base import logical_effort from tech import cell_properties as props from tech import spice, parameter -class inv_dec(design.design): +class inv_dec(design): """ INV for address decoders. """ @@ -51,12 +51,12 @@ class inv_dec(design.design): Input inverted by this stage. """ parasitic_delay = 1 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 6049f3b1..ba253e9d 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -5,15 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import bitcell_base_array +from .bitcell_base_array import bitcell_base_array from globals import OPTS from sram_factory import factory -from vector import vector +from base import vector import debug from tech import layer_properties as layer_props -class local_bitcell_array(bitcell_base_array.bitcell_base_array): +class local_bitcell_array(bitcell_base_array): """ A local bitcell array is a bitcell array with a wordline driver. This can either be a single aray on its own if there is no hierarchical WL @@ -73,12 +73,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): rbl=self.rbl, left_rbl=self.left_rbl, right_rbl=self.right_rbl) - self.add_mod(self.bitcell_array) self.wl_array = factory.create(module_type="wordline_buffer_array", rows=self.rows + 1, cols=self.cols) - self.add_mod(self.wl_array) def add_pins(self): # Outputs from the wordline driver (by port) @@ -161,17 +159,20 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): def place(self): """ Place the bitcelll array to the right of the wl driver. """ - # FIXME: Replace this with a tech specific paramter + + # FIXME: Replace this with a tech specific parameter driver_to_array_spacing = 3 * self.m3_pitch - self.wl_insts[0].place(vector(0, self.cell.height)) + wl_offset = vector(0, self.bitcell_array.get_replica_bottom()) + self.wl_insts[0].place(wl_offset) - self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing, - 0)) + bitcell_array_offset = vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0) + self.bitcell_array_inst.place(bitcell_array_offset) if len(self.all_ports) > 1: - self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, - 2 * self.cell.height + self.wl_array.height), + wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, + self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height) + self.wl_insts[1].place(wl_offset, mirror="XY") self.height = self.bitcell_array.height diff --git a/compiler/modules/multibank.py b/compiler/modules/multibank.py index 408c91c5..e5c4bbfc 100644 --- a/compiler/modules/multibank.py +++ b/compiler/modules/multibank.py @@ -8,15 +8,14 @@ import sys from tech import drc, parameter import debug -import design +from base import design import math from math import log,sqrt,ceil -import contact -from vector import vector +from base import vector from sram_factory import factory from globals import OPTS -class multibank(design.design): +class multibank(design): """ Dynamically generated a single bank including bitcell array, hierarchical_decoder, precharge, (optional column_mux and column decoder), @@ -172,47 +171,36 @@ class multibank(design.design): self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols, rows=self.num_rows) - self.add_mod(self.bitcell_array) self.precharge_array = self.mod_precharge_array(columns=self.num_cols) - self.add_mod(self.precharge_array) if self.col_addr_size > 0: self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols, word_size=self.word_size) - self.add_mod(self.column_mux_array) self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size, words_per_row=self.words_per_row) - self.add_mod(self.sense_amp_array) if self.write_size: self.write_mask_driver_array = self.mod_write_mask_driver_array(columns=self.num_cols, word_size=self.word_size, write_size=self.write_size) - self.add_mod(self.write_mask_driver_array) else: self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols, word_size=self.word_size) - self.add_mod(self.write_driver_array) self.row_decoder = self.mod_decoder(rows=self.num_rows) - self.add_mod(self.row_decoder) self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, word_size=self.word_size) - self.add_mod(self.tri_gate_array) self.wordline_driver = self.mod_wordline_driver(rows=self.num_rows) - self.add_mod(self.wordline_driver) self.inv = pinv() - self.add_mod(self.inv) if(self.num_banks > 1): self.bank_select = self.mod_bank_select() - self.add_mod(self.bank_select) def add_bitcell_array(self): @@ -810,7 +798,7 @@ class multibank(design.design): self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) # Bring it up to M2 for M2/M3 routing self.add_via(layers=self.m1_stack, - offset=in_pin + contact.m1_via.offset, + offset=in_pin + self.m1_via.offset, rotate=90) self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, @@ -823,7 +811,7 @@ class multibank(design.design): rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y) self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)]) self.add_via(layers=self.m1_stack, - offset=in_pin + contact.m1_via.offset, + offset=in_pin + self.m1_via.offset, rotate=90) self.add_via(layers=self.m2_stack, offset=in_pin + self.m2m3_via_offset, diff --git a/compiler/custom/nand2_dec.py b/compiler/modules/nand2_dec.py similarity index 87% rename from compiler/custom/nand2_dec.py rename to compiler/modules/nand2_dec.py index 33b7729b..493facb8 100644 --- a/compiler/custom/nand2_dec.py +++ b/compiler/modules/nand2_dec.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design from tech import spice, parameter, drc from tech import cell_properties as props -import logical_effort +from base import logical_effort -class nand2_dec(design.design): +class nand2_dec(design): """ 2-input NAND decoder for address decoders. """ @@ -56,12 +56,12 @@ class nand2_dec(design.design): Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -96,4 +96,4 @@ class nand2_dec(design.design): pmos_drain_c = self.drain_c_(self.pmos_width*mult, 1, mult) - return nmos_drain_c + pmos_drain_c \ No newline at end of file + return nmos_drain_c + pmos_drain_c diff --git a/compiler/custom/nand3_dec.py b/compiler/modules/nand3_dec.py similarity index 87% rename from compiler/custom/nand3_dec.py rename to compiler/modules/nand3_dec.py index af0d2f1f..08978d46 100644 --- a/compiler/custom/nand3_dec.py +++ b/compiler/modules/nand3_dec.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design from tech import spice, parameter, drc from tech import cell_properties as props -import logical_effort +from base import logical_effort -class nand3_dec(design.design): +class nand3_dec(design): """ 3-input NAND decoder for address decoders. """ @@ -56,12 +56,12 @@ class nand3_dec(design.design): Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -96,4 +96,4 @@ class nand3_dec(design.design): pmos_drain_c = self.drain_c_(self.pmos_width*mult, 1, mult) - return nmos_drain_c + pmos_drain_c \ No newline at end of file + return nmos_drain_c + pmos_drain_c diff --git a/compiler/custom/nand4_dec.py b/compiler/modules/nand4_dec.py similarity index 87% rename from compiler/custom/nand4_dec.py rename to compiler/modules/nand4_dec.py index a4afadbe..8dd4a039 100644 --- a/compiler/custom/nand4_dec.py +++ b/compiler/modules/nand4_dec.py @@ -5,13 +5,13 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design from tech import spice, parameter, drc from tech import cell_properties as props -import logical_effort +from base import logical_effort -class nand4_dec(design.design): +class nand4_dec(design): """ 4-input NAND decoder for address decoders. """ @@ -56,12 +56,12 @@ class nand4_dec(design.design): Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -96,4 +96,4 @@ class nand4_dec(design.design): pmos_drain_c = self.drain_c_(self.pmos_width*mult, 1, mult) - return nmos_drain_c + pmos_drain_c \ No newline at end of file + return nmos_drain_c + pmos_drain_c diff --git a/compiler/modules/orig_bitcell_array.py b/compiler/modules/orig_bitcell_array.py index 42ebdc33..2e3088af 100644 --- a/compiler/modules/orig_bitcell_array.py +++ b/compiler/modules/orig_bitcell_array.py @@ -5,7 +5,7 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from bitcell_base_array import bitcell_base_array +from .bitcell_base_array import bitcell_base_array from tech import drc, spice from globals import OPTS from sram_factory import factory @@ -47,7 +47,6 @@ class bitcell_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ self.cell = factory.create(module_type=OPTS.bitcell) - self.add_mod(self.cell) def create_instances(self): """ Create the module instances used in this design """ diff --git a/compiler/pgates/pand2.py b/compiler/modules/pand2.py similarity index 96% rename from compiler/pgates/pand2.py rename to compiler/modules/pand2.py index 48b5d3a7..8bd17589 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/modules/pand2.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -from vector import vector -import pgate +from base import vector +from .pgate import * from sram_factory import factory -class pand2(pgate.pgate): +class pand2(pgate): """ This is an AND (or NAND) with configurable drive strength. """ @@ -32,16 +32,13 @@ class pand2(pgate.pgate): def create_modules(self): self.nand = factory.create(module_type="pnand2", height=self.height, - add_wells=self.vertical) + add_wells=False) self.inv = factory.create(module_type="pdriver", size_list=[self.size], height=self.height, add_wells=self.add_wells) - self.add_mod(self.nand) - self.add_mod(self.inv) - def create_layout(self): if self.vertical: self.height = 2 * self.nand.height @@ -146,8 +143,8 @@ class pand2(pgate.pgate): offset=pin.center(), width=pin.width(), height=pin.height()) - + def is_non_inverting(self): """Return input to output polarity for module""" - - return True \ No newline at end of file + + return True diff --git a/compiler/pgates/pand3.py b/compiler/modules/pand3.py similarity index 97% rename from compiler/pgates/pand3.py rename to compiler/modules/pand3.py index 713178cc..f63b8c41 100644 --- a/compiler/pgates/pand3.py +++ b/compiler/modules/pand3.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -from vector import vector -import pgate +from base import vector +from .pgate import * from sram_factory import factory -class pand3(pgate.pgate): +class pand3(pgate): """ This is a simple buffer used for driving loads. """ @@ -43,9 +43,6 @@ class pand3(pgate.pgate): height=self.height, add_wells=self.add_wells) - self.add_mod(self.nand) - self.add_mod(self.inv) - def create_layout(self): if self.vertical: self.height = 2 * self.nand.height diff --git a/compiler/pgates/pand4.py b/compiler/modules/pand4.py similarity index 97% rename from compiler/pgates/pand4.py rename to compiler/modules/pand4.py index 63eb1133..9b5a31d6 100644 --- a/compiler/pgates/pand4.py +++ b/compiler/modules/pand4.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -from vector import vector -import pgate +from base import vector +from .pgate import * from sram_factory import factory -class pand4(pgate.pgate): +class pand4(pgate): """ This is a simple buffer used for driving loads. """ @@ -43,9 +43,6 @@ class pand4(pgate.pgate): height=self.height, add_wells=self.add_wells) - self.add_mod(self.nand) - self.add_mod(self.inv) - def create_layout(self): if self.vertical: self.height = 2 * self.nand.height @@ -162,4 +159,3 @@ class pand4(pgate.pgate): slew=nand_delay.slew, load=load) return nand_delay + inv_delay - diff --git a/compiler/bitcells/pbitcell.py b/compiler/modules/pbitcell.py similarity index 92% rename from compiler/bitcells/pbitcell.py rename to compiler/modules/pbitcell.py index 71679bd1..516dca3f 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/modules/pbitcell.py @@ -5,18 +5,17 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact import debug +from base import logical_effort +from base import vector from tech import drc, parameter, layer from tech import cell_properties as props -from vector import vector -from ptx import ptx from globals import OPTS -import logical_effort -import bitcell_base +from .ptx import ptx +from .bitcell_base import bitcell_base -class pbitcell(bitcell_base.bitcell_base): +class pbitcell(bitcell_base): """ This module implements a parametrically sized multi-port bitcell, with a variable number of read/write, write, and read ports @@ -33,7 +32,19 @@ class pbitcell(bitcell_base.bitcell_base): self.mirror = props.bitcell_1port.mirror self.end_caps = props.bitcell_1port.end_caps - bitcell_base.bitcell_base.__init__(self, name) + self.wl_layer = "m1" + self.wl_dir = "H" + + self.bl_layer = "m2" + self.bl_dir = "V" + + self.vdd_layer = "m1" + self.vdd_dir = "H" + + self.gnd_layer = "m1" + self.gnd_dir = "H" + + bitcell_base.__init__(self, name) fmt_str = "{0} rw ports, {1} w ports and {2} r ports" info_string = fmt_str.format(self.num_rw_ports, self.num_w_ports, @@ -87,7 +98,7 @@ class pbitcell(bitcell_base.bitcell_base): self.route_wordlines() self.route_bitlines() - self.route_supply() + self.route_supplies() if self.replica_bitcell: self.route_rbc_short() @@ -179,26 +190,21 @@ class pbitcell(bitcell_base.bitcell_base): # create ptx for inverter transistors self.inverter_nmos = ptx(width=inverter_nmos_width, tx_type="nmos") - self.add_mod(self.inverter_nmos) self.inverter_pmos = ptx(width=inverter_pmos_width, tx_type="pmos") - self.add_mod(self.inverter_pmos) # create ptx for readwrite transitors self.readwrite_nmos = ptx(width=readwrite_nmos_width, tx_type="nmos") - self.add_mod(self.readwrite_nmos) # create ptx for write transitors self.write_nmos = ptx(width=write_nmos_width, tx_type="nmos") - self.add_mod(self.write_nmos) # create ptx for read transistors self.read_nmos = ptx(width=read_nmos_width, tx_type="nmos") - self.add_mod(self.read_nmos) def calculate_spacing(self): """ Calculate transistor spacings """ @@ -215,20 +221,20 @@ class pbitcell(bitcell_base.bitcell_base): # y-offset for the access transistor's gate contact self.gate_contact_yoffset = max_contact_extension + self.m2_space \ - + 0.5 * max(contact.poly_contact.height, contact.m1_via.height) + + 0.5 * max(self.poly_contact.height, self.m1_via.height) # y-position of access transistors - self.port_ypos = self.m1_space + 0.5 * contact.m1_via.height + self.gate_contact_yoffset + self.port_ypos = self.m1_space + 0.5 * self.m1_via.height + self.gate_contact_yoffset # y-position of inverter nmos self.inverter_nmos_ypos = self.port_ypos # spacing between ports (same for read/write and write ports) self.bitline_offset = -0.5 * self.readwrite_nmos.active_width \ - + 0.5 * contact.m1_via.height \ + + 0.5 * self.m1_via.height \ + self.m2_space + self.m2_width m2_constraint = self.bitline_offset + self.m2_space \ - + 0.5 * contact.m1_via.height \ + + 0.5 * self.m1_via.height \ - 0.5 * self.readwrite_nmos.active_width self.write_port_spacing = max(self.active_space, self.m1_space, @@ -236,7 +242,7 @@ class pbitcell(bitcell_base.bitcell_base): self.read_port_spacing = self.bitline_offset + self.m2_space # spacing between cross coupled inverters - self.inverter_to_inverter_spacing = contact.poly_contact.width + self.m1_space + self.inverter_to_inverter_spacing = self.poly_contact.width + self.m1_space # calculations related to inverter connections inverter_pmos_contact_extension = 0.5 * \ @@ -245,19 +251,19 @@ class pbitcell(bitcell_base.bitcell_base): (self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height) self.inverter_gap = max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + self.poly_to_contact + 2 * contact.poly_contact.width \ + + self.poly_to_contact + 2 * self.poly_contact.width \ + self.m1_space + inverter_pmos_contact_extension self.cross_couple_lower_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ - + 0.5 * contact.poly_contact.width + + 0.5 * self.poly_contact.width self.cross_couple_upper_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ + max(self.poly_to_active, self.m1_space + inverter_nmos_contact_extension) \ + self.poly_to_contact \ - + 1.5 * contact.poly_contact.width + + 1.5 * self.poly_contact.width # spacing between wordlines (and gnd) self.m1_offset = -0.5 * self.m1_width @@ -293,7 +299,7 @@ class pbitcell(bitcell_base.bitcell_base): (self.write_nmos.active_width + self.write_port_spacing) \ - self.num_r_ports * \ (self.read_port_width + self.read_port_spacing) \ - - self.bitline_offset - 0.5 * contact.m1_via.width + - self.bitline_offset - 0.5 * self.m1_via.width self.width = -2 * self.leftmost_xpos self.height = self.topmost_ypos - self.botmost_ypos @@ -383,14 +389,14 @@ class pbitcell(bitcell_base.bitcell_base): # add contacts to connect gate poly to drain/source # metal1 (to connect Q to Q_bar) contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \ - + 0.5 * contact.poly_contact.height, + + 0.5 * self.poly_contact.height, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=contact_offset_left, directions=("H", "H")) contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - - 0.5*contact.poly_contact.height, + - 0.5*self.poly_contact.height, self.cross_couple_lower_ypos) self.add_via_center(layers=self.poly_stack, offset=contact_offset_right, @@ -407,24 +413,24 @@ class pbitcell(bitcell_base.bitcell_base): if OPTS.use_pex: # add labels to cross couple inverter for extracted simulation contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \ - + 0.5 * contact.poly.height, + + 0.5 * self.poly.height, self.cross_couple_upper_ypos) contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \ - - 0.5*contact.poly.height, + - 0.5*self.poly.height, self.cross_couple_lower_ypos) self.add_pex_labels(contact_offset_left_output, contact_offset_right_output) def route_rails(self): """ Adds gnd and vdd rails and connects them to the inverters """ + # Add rails for vdd and gnd gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch self.gnd_position = vector(0, gnd_ypos) - self.add_rect_center(layer="m1", - offset=self.gnd_position, - width=self.width) - self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H")) - + self.add_layout_pin_rect_center(text="gnd", + layer="m1", + offset=self.gnd_position, + width=self.width) vdd_ypos = self.inverter_nmos_ypos \ + self.inverter_nmos.active_height \ @@ -432,10 +438,10 @@ class pbitcell(bitcell_base.bitcell_base): + self.inverter_pmos.active_height \ + self.vdd_offset self.vdd_position = vector(0, vdd_ypos) - self.add_rect_center(layer="m1", - offset=self.vdd_position, - width=self.width) - self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H")) + self.add_layout_pin_rect_center(text="vdd", + layer="m1", + offset=self.vdd_position, + width=self.width) def create_readwrite_ports(self): """ @@ -475,6 +481,7 @@ class pbitcell(bitcell_base.bitcell_base): self.connect_inst([self.Q_bar, self.rw_wl_names[k], br_name, "gnd"]) + def place_readwrite_ports(self): """ Places read/write ports in the bit cell """ # define read/write transistor variables as empty arrays @@ -529,6 +536,21 @@ class pbitcell(bitcell_base.bitcell_base): offset=self.rwbr_positions[k], height=self.height) + if self.dummy_bitcell: + bl_name = self.rw_bl_names[k] + br_name = self.rw_br_names[k] + bl_name += "_noconn" + br_name += "_noconn" + + # This helps with LVS matching in klayout + drain_pin = self.readwrite_nmos_left[k].get_pin("S") + self.add_label(bl_name, drain_pin.layer, drain_pin.center()) + + # This helps with LVS matching in klayout + source_pin = self.readwrite_nmos_right[k].get_pin("D") + self.add_label(br_name, source_pin.layer, source_pin.center()) + + # update furthest left and right transistor edges self.left_building_edge = left_readwrite_transistor_xpos self.right_building_edge = right_readwrite_transistor_xpos \ @@ -626,6 +648,20 @@ class pbitcell(bitcell_base.bitcell_base): offset=self.wbr_positions[k], height=self.height) + if self.dummy_bitcell: + bl_name = self.w_bl_names[k] + br_name = self.w_br_names[k] + bl_name += "_noconn" + br_name += "_noconn" + + # This helps with LVS matching in klayout + drain_pin = self.write_nmos_left[k].get_pin("S") + self.add_label(bl_name, drain_pin.layer, drain_pin.center()) + + # This helps with LVS matching in klayout + source_pin = self.write_nmos_right[k].get_pin("D") + self.add_label(br_name, source_pin.layer, source_pin.center()) + # update furthest left and right transistor edges self.left_building_edge = left_write_transistor_xpos self.right_building_edge = right_write_transistor_xpos \ @@ -753,6 +789,20 @@ class pbitcell(bitcell_base.bitcell_base): offset=self.rbr_positions[k], height=self.height) + if self.dummy_bitcell: + bl_name = self.r_bl_names[k] + br_name = self.r_br_names[k] + bl_name += "_noconn" + br_name += "_noconn" + + # This helps with LVS matching in klayout + drain_pin = self.read_access_nmos_left[k].get_pin("S") + self.add_label(bl_name, drain_pin.layer, drain_pin.center()) + + # This helps with LVS matching in klayout + source_pin = self.read_access_nmos_right[k].get_pin("D") + self.add_label(br_name, source_pin.layer, source_pin.center()) + def route_wordlines(self): """ Routes gate of transistors to their respective wordlines """ port_transistors = [] @@ -845,7 +895,7 @@ class pbitcell(bitcell_base.bitcell_base): directions="nonpref") self.add_path("m2", - [port_contact_offest, bl_offset], width=contact.m1_via.height) + [port_contact_offest, bl_offset], width=self.m1_via.height) for k in range(self.total_ports): port_contact_offest = right_port_transistors[k].get_pin("D").center() @@ -858,9 +908,9 @@ class pbitcell(bitcell_base.bitcell_base): directions="nonpref") self.add_path("m2", - [port_contact_offest, br_offset], width=contact.m1_via.height) + [port_contact_offest, br_offset], width=self.m1_via.height) - def route_supply(self): + def route_supplies(self): """ Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """ # route inverter nmos and read-access nmos to gnd nmos_contact_positions = [] @@ -877,9 +927,9 @@ class pbitcell(bitcell_base.bitcell_base): if position.x > 0: - contact_correct = 0.5 * contact.m1_via.height + contact_correct = 0.5 * self.m1_via.height else: - contact_correct = -0.5 * contact.m1_via.height + contact_correct = -0.5 * self.m1_via.height supply_offset = vector(position.x + contact_correct, self.gnd_position.y) self.add_via_center(layers=self.m1_stack, @@ -946,7 +996,7 @@ class pbitcell(bitcell_base.bitcell_base): """ # add poly to metal1 contacts for gates of the inverters left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \ - - self.poly_to_contact - 0.5*contact.poly_contact.width, + - self.poly_to_contact - 0.5*self.poly_contact.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=left_storage_contact, @@ -954,7 +1004,7 @@ class pbitcell(bitcell_base.bitcell_base): right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \ - + self.poly_to_contact + 0.5*contact.poly_contact.width, + + self.poly_to_contact + 0.5*self.poly_contact.width, self.cross_couple_upper_ypos) self.add_via_center(layers=self.poly_stack, offset=right_storage_contact, @@ -1013,7 +1063,7 @@ class pbitcell(bitcell_base.bitcell_base): well_height = max_nmos_well_height + self.port_ypos \ - self.nwell_enclose_active - self.gnd_position.y # FIXME fudge factor xpos - well_width = self.width + 2*self.nwell_enclose_active + well_width = self.width + 2 * self.nwell_enclose_active offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos) self.add_rect(layer="pwell", offset=offset, @@ -1141,12 +1191,12 @@ class pbitcell(bitcell_base.bitcell_base): # min size NMOS gate load read_port_load = self.num_r_ports / 2 total_load = load + read_port_load + write_port_load - return logical_effort.logical_effort('bitline', - size, - cin, - load + read_port_load, - parasitic_delay, - False) + return logical_effort('bitline', + size, + cin, + load + read_port_load, + parasitic_delay, + False) def input_load(self): """ Return the relative capacitance of the access transistor gates """ @@ -1163,7 +1213,7 @@ class pbitcell(bitcell_base.bitcell_base): return pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} - + # Edges added wl->bl, wl->br for every port except write ports rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) r_pin_names = zip(self.rw_wl_names, self.rw_bl_names, self.rw_br_names) @@ -1172,4 +1222,3 @@ class pbitcell(bitcell_base.bitcell_base): for wl, bl, br in pin_zip: graph.add_edge(pin_dict[wl], pin_dict[bl], self) graph.add_edge(pin_dict[wl], pin_dict[br], self) - diff --git a/compiler/pgates/pbuf.py b/compiler/modules/pbuf.py similarity index 96% rename from compiler/pgates/pbuf.py rename to compiler/modules/pbuf.py index 22a28bb9..ba44fe2f 100644 --- a/compiler/pgates/pbuf.py +++ b/compiler/modules/pbuf.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -from vector import vector -import pgate +from base import vector +from .pgate import * from sram_factory import factory -class pbuf(pgate.pgate): +class pbuf(pgate): """ This is a simple buffer used for driving loads. """ @@ -52,13 +52,11 @@ class pbuf(pgate.pgate): self.inv1 = factory.create(module_type="pinv", size=input_size, height=self.height) - self.add_mod(self.inv1) self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.height, add_wells=False) - self.add_mod(self.inv2) def create_insts(self): self.inv1_inst = self.add_inst(name="buf_inv1", @@ -96,4 +94,3 @@ class pbuf(pgate.pgate): offset=a_pin.center(), width=a_pin.width(), height=a_pin.height()) - diff --git a/compiler/pgates/pbuf_dec.py b/compiler/modules/pbuf_dec.py similarity index 95% rename from compiler/pgates/pbuf_dec.py rename to compiler/modules/pbuf_dec.py index 8552e265..c04d4922 100644 --- a/compiler/pgates/pbuf_dec.py +++ b/compiler/modules/pbuf_dec.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -from vector import vector -import pgate +from base import vector +from .pgate import * from sram_factory import factory -class pbuf_dec(pgate.pgate): +class pbuf_dec(pgate): """ This is a simple buffer used for driving wordlines. """ @@ -25,7 +25,7 @@ class pbuf_dec(pgate.pgate): self.height = height # Creates the netlist and layout - pgate.pgate.__init__(self, name, height) + pgate.__init__(self, name, height) def create_netlist(self): self.add_pins() @@ -52,12 +52,10 @@ class pbuf_dec(pgate.pgate): self.inv1 = factory.create(module_type="pinv_dec", size=input_size, height=self.height) - self.add_mod(self.inv1) self.inv2 = factory.create(module_type="pinv_dec", size=self.size, height=self.height) - self.add_mod(self.inv2) def create_insts(self): self.inv1_inst = self.add_inst(name="buf_inv1", diff --git a/compiler/pgates/pdriver.py b/compiler/modules/pdriver.py similarity index 98% rename from compiler/pgates/pdriver.py rename to compiler/modules/pdriver.py index bbadb9ab..a9241af5 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/modules/pdriver.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -import pgate -from vector import vector +from .pgate import * +from base import vector from sram_factory import factory -class pdriver(pgate.pgate): +class pdriver(pgate): """ This instantiates an even or odd number of inverters sized for driving a load. @@ -93,7 +93,6 @@ class pdriver(pgate.pgate): height=self.height, add_wells=self.add_wells) self.inv_list.append(temp_inv) - self.add_mod(temp_inv) def create_insts(self): self.inv_inst_list = [] diff --git a/compiler/pgates/pgate.py b/compiler/modules/pgate.py similarity index 89% rename from compiler/pgates/pgate.py rename to compiler/modules/pgate.py index 19a027e9..ad61cb68 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/modules/pgate.py @@ -5,20 +5,20 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact -import design +from base import design +from base import vector import debug import math from bisect import bisect_left from tech import layer, drc -from vector import vector from globals import OPTS from tech import cell_properties as cell_props + if cell_props.ptx.bin_spice_models: from tech import nmos_bins, pmos_bins -class pgate(design.design): +class pgate(design): """ This is a module that implements some shared functions for parameterized gates. @@ -113,14 +113,14 @@ class pgate(design.design): left_gate_offset = vector(nmos_gate_pin.lx(), ypos) # Center is completely symmetric. - contact_width = contact.poly_contact.width + contact_width = self.poly_contact.width if position == "center": contact_offset = left_gate_offset \ + vector(0.5 * self.poly_width, 0) elif position == "farleft": contact_offset = left_gate_offset \ - - vector(0.5 * contact.poly_contact.width, 0) + - vector(0.5 * self.poly_contact.width, 0) elif position == "left": contact_offset = left_gate_offset \ - vector(0.5 * contact_width - 0.5 * self.poly_width, 0) @@ -146,7 +146,7 @@ class pgate(design.design): + left_gate_offset.scale(0.5, 0) self.add_rect_center(layer="poly", offset=mid_point, - height=contact.poly_contact.first_layer_width, + height=self.poly_contact.first_layer_width, width=left_gate_offset.x - contact_offset.x) return via @@ -208,12 +208,15 @@ class pgate(design.design): # from the top of the well # OR align the active with the top of PMOS active. max_y_offset = self.height + 0.5 * self.m1_width - contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height, - max_y_offset - pmos.active_contact.first_layer_height / 2 - self.nwell_enclose_active) + contact_yoffset = min(self.height - 0.5 * self.implant_width, + self.get_tx_insts("pmos")[0].uy()) \ + - pmos.active_contact.first_layer_height \ + - self.implant_enclose_active contact_offset = vector(contact_xoffset, contact_yoffset) # Offset by half a contact in x and y contact_offset += vector(0.5 * pmos.active_contact.first_layer_width, 0.5 * pmos.active_contact.first_layer_height) + # This over-rides the default one with a custom direction self.nwell_contact = self.add_via_center(layers=layer_stack, offset=contact_offset, implant_type="n", @@ -276,24 +279,34 @@ class pgate(design.design): rightx=rightx, topy=self.height) - try: - ntap_insts = [self.nwell_contact] - self.add_enclosure(ntap_insts, - layer="nimplant", - extend=self.implant_enclose_active, - rightx=self.width, - topy=self.height) - except AttributeError: - pass - try: - ptap_insts = [self.pwell_contact] - self.add_enclosure(ptap_insts, - layer="pimplant", - extend=self.implant_enclose_active, - rightx=self.width, - boty=0) - except AttributeError: - pass + self.add_rect(layer="pimplant", + offset=vector(0, self.height - 0.5 * self.implant_width), + width=self.width, + height=self.implant_width) + self.add_rect(layer="nimplant", + offset=vector(0, -0.5 * self.implant_width), + width=self.width, + height=self.implant_width) + + +# try: +# ntap_insts = [self.nwell_contact] +# self.add_enclosure(ntap_insts, +# layer="nimplant", +# extend=self.implant_enclose_active, +# rightx=self.width, +# topy=self.height) +# except AttributeError: +# pass +# try: +# ptap_insts = [self.pwell_contact] +# self.add_enclosure(ptap_insts, +# layer="pimplant", +# extend=self.implant_enclose_active, +# rightx=self.width, +# boty=0) +# except AttributeError: +# pass def add_pwell_contact(self, nmos, nmos_pos): """ Add an pwell contact next to the given nmos device. """ @@ -303,11 +316,9 @@ class pgate(design.design): # To the right a spacing away from the nmos right active edge contact_xoffset = nmos_pos.x + nmos.active_width \ + self.active_space - # Must be at least an well enclosure of active up - # from the bottom of the well - contact_yoffset = max(nmos_pos.y, - self.nwell_enclose_active \ - - nmos.active_contact.first_layer_height / 2) + # Allow an nimplant below it under the rail + contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active, + self.get_tx_insts("nmos")[0].by()) contact_offset = vector(contact_xoffset, contact_yoffset) # Offset by half a contact @@ -362,7 +373,7 @@ class pgate(design.design): # It was already set or is left as default (minimum) # Width is determined by well contact and spacing and allowing a supply via between each cell if self.add_wells: - width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width + width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * self.m1_via.width # Height is an input parameter, so it is not recomputed. else: max_active_xoffset = self.find_highest_layer_coords("active").x diff --git a/compiler/pgates/pinv.py b/compiler/modules/pinv.py similarity index 92% rename from compiler/pgates/pinv.py rename to compiler/modules/pinv.py index a3e467b7..a60bed13 100644 --- a/compiler/pgates/pinv.py +++ b/compiler/modules/pinv.py @@ -5,22 +5,21 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact -import pgate import debug +from .pgate import * +from base import vector +from base import logical_effort +from base.utils import round_to_grid +from base.errors import drc_error import operator from tech import drc, parameter, spice -from vector import vector from math import ceil from globals import OPTS -from utils import round_to_grid -import logical_effort from sram_factory import factory -from errors import drc_error from tech import cell_properties as cell_props -class pinv(pgate.pgate): +class pinv(pgate): """ Pinv generates gds of a parametrically sized inverter. The size is specified as the drive size (relative to minimum NMOS) and @@ -89,8 +88,8 @@ class pinv(pgate.pgate): self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") if cell_props.ptx.bin_spice_models: - (self.nmos_width, self.tx_mults) = pgate.pgate.best_bin("nmos", self.nmos_width) - (self.pmos_width, self.tx_mults) = pgate.pgate.best_bin("pmos", self.pmos_width) + (self.nmos_width, self.tx_mults) = pgate.best_bin("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = pgate.best_bin("pmos", self.pmos_width) return # Do a quick sanity check and bail if unlikely feasible height @@ -106,8 +105,8 @@ class pinv(pgate.pgate): tx_type="pmos") tx_height = nmos.poly_height + pmos.poly_height # rotated m1 pitch or poly to active spacing - min_channel = max(contact.poly_contact.width + self.m1_space, - contact.poly_contact.width + 2 * self.poly_to_active) + min_channel = max(self.poly_contact.width + self.m1_space, + self.poly_contact.width + 2 * self.poly_to_active) total_height = tx_height + min_channel + 2 * self.top_bottom_space # debug.check(self.height > total_height, @@ -207,7 +206,6 @@ class pinv(pgate.pgate): add_drain_contact=self.route_layer, connect_poly=True, connect_drain_active=True) - self.add_mod(self.nmos) self.pmos = factory.create(module_type="ptx", width=self.pmos_width, @@ -217,7 +215,6 @@ class pinv(pgate.pgate): add_drain_contact=self.route_layer, connect_poly=True, connect_drain_active=True) - self.add_mod(self.pmos) def create_ptx(self): """ @@ -324,12 +321,12 @@ class pinv(pgate.pgate): Input inverted by this stage. """ parasitic_delay = 1 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -337,30 +334,29 @@ class pinv(pgate.pgate): Overrides base class function. """ self.add_graph_edges(graph, port_nets) - + def is_non_inverting(self): """Return input to output polarity for module""" - - return False + return False def get_on_resistance(self): """On resistance of pinv, defined by single nmos""" is_nchannel = True stack = 1 is_cell = False - return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell) + return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell) def get_input_capacitance(self): """Input cap of input, passes width of gates to gate cap function""" - return self.gate_c(self.nmos_width+self.pmos_width) - + return self.gate_c(self.nmos_width+self.pmos_width) + def get_intrinsic_capacitance(self): """Get the drain capacitances of the TXs in the gate.""" nmos_stack = 1 - nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults, + nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults, nmos_stack, self.tx_mults) - pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, + pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, 1, - self.tx_mults) - return nmos_drain_c + pmos_drain_c \ No newline at end of file + self.tx_mults) + return nmos_drain_c + pmos_drain_c diff --git a/compiler/pgates/pinv_dec.py b/compiler/modules/pinv_dec.py similarity index 92% rename from compiler/pgates/pinv_dec.py rename to compiler/modules/pinv_dec.py index b738f5fb..7b378f08 100644 --- a/compiler/pgates/pinv_dec.py +++ b/compiler/modules/pinv_dec.py @@ -5,17 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact -import pinv import debug +from base import vector +from .pinv import pinv from tech import drc, parameter, layer -from vector import vector from globals import OPTS from sram_factory import factory from tech import cell_properties as cell_props -class pinv_dec(pinv.pinv): +class pinv_dec(pinv): """ This is another version of pinv but with layout for the decoder. Other stuff is the same (netlist, sizes, etc.) @@ -78,7 +77,7 @@ class pinv_dec(pinv.pinv): self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) # Center is completely symmetric. - contact_width = contact.poly_contact.width + contact_width = self.poly_contact.width contact_offset = nmos_gate_pin.lc() \ - vector(self.poly_extend_active + 0.5 * contact_width, 0) via = self.add_via_stack_center(from_layer="poly", @@ -126,7 +125,7 @@ class pinv_dec(pinv.pinv): y_offset = (0.5 * (self.height - self.nmos.width) + self.nmos.width) * 0.9 # offset so that the input contact is over from the left edge by poly spacing - x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space + x_offset = self.nmos.active_offset.y + self.poly_contact.width + self.poly_space self.nmos_pos = vector(x_offset, y_offset) self.nmos_inst.place(self.nmos_pos, rotate=270) @@ -192,20 +191,20 @@ class pinv_dec(pinv.pinv): source_pos = self.pmos_inst.get_pin("S").center() contact_pos = vector(source_pos.x, self.height) - self.nwell_contact = self.add_via_center(layers=self.active_stack, - offset=contact_pos, - implant_type="n", - well_type="n") + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="n", + well_type="n") self.add_via_stack_center(offset=contact_pos, from_layer=self.active_stack[2], to_layer=self.supply_layer) source_pos = self.nmos_inst.get_pin("S").center() contact_pos = vector(source_pos.x, self.height) - self.pwell_contact= self.add_via_center(layers=self.active_stack, - offset=contact_pos, - implant_type="p", - well_type="p") + self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") self.add_via_stack_center(offset=contact_pos, from_layer=self.active_stack[2], to_layer=self.supply_layer) diff --git a/compiler/pgates/pinvbuf.py b/compiler/modules/pinvbuf.py similarity index 97% rename from compiler/pgates/pinvbuf.py rename to compiler/modules/pinvbuf.py index d232cabe..78dca7b5 100644 --- a/compiler/pgates/pinvbuf.py +++ b/compiler/modules/pinvbuf.py @@ -6,12 +6,12 @@ # All rights reserved. # import debug -import pgate -from vector import vector +from .pgate import * +from base import vector from sram_factory import factory from tech import layer -class pinvbuf(pgate.pgate): +class pinvbuf(pgate): """ This is a simple inverter/buffer used for driving loads. It is used in the column decoder for 1:2 decoding and as the clock buffer. @@ -65,17 +65,14 @@ class pinvbuf(pgate.pgate): self.inv = factory.create(module_type="pinv", size=input_size, height=self.row_height) - self.add_mod(self.inv) self.inv1 = factory.create(module_type="pinv", size=self.predriver_size, height=self.row_height) - self.add_mod(self.inv1) self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.row_height) - self.add_mod(self.inv2) def create_insts(self): # Create INV1 (capacitance shield) diff --git a/compiler/pgates/pnand2.py b/compiler/modules/pnand2.py similarity index 91% rename from compiler/pgates/pnand2.py rename to compiler/modules/pnand2.py index 1fafc210..9262b2f8 100644 --- a/compiler/pgates/pnand2.py +++ b/compiler/modules/pnand2.py @@ -5,17 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import pgate +from .pgate import * import debug from tech import drc, parameter, spice -from vector import vector -import logical_effort +from base import vector +from base import logical_effort from sram_factory import factory -import contact from tech import cell_properties as cell_props -class pnand2(pgate.pgate): +class pnand2(pgate): """ This module generates gds of a parametrically sized 2-input nand. This model use ptx to generate a 2-input nand within a cetrain height. @@ -79,7 +78,6 @@ class pnand2(pgate.pgate): tx_type="nmos", add_source_contact=self.route_layer, add_drain_contact="active") - self.add_mod(self.nmos_left) self.nmos_right = factory.create(module_type="ptx", width=self.nmos_width, @@ -87,7 +85,6 @@ class pnand2(pgate.pgate): tx_type="nmos", add_source_contact="active", add_drain_contact=self.route_layer) - self.add_mod(self.nmos_right) self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, @@ -95,7 +92,6 @@ class pnand2(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_left) self.pmos_right = factory.create(module_type="ptx", width=self.pmos_width, @@ -103,7 +99,6 @@ class pnand2(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_right) def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ @@ -179,11 +174,11 @@ class pnand2(pgate.pgate): # Top of NMOS drain bottom_pin = self.nmos1_inst.get_pin("D") # active contact metal to poly contact metal spacing - active_contact_to_poly_contact = bottom_pin.uy() + self.route_layer_space + 0.5 * contact.poly_contact.second_layer_height + active_contact_to_poly_contact = bottom_pin.uy() + self.route_layer_space + 0.5 * self.poly_contact.second_layer_height # active diffusion to poly contact spacing # doesn't use nmos uy because that is calculated using offset + poly height active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height - active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + active_to_poly_contact = active_top + self.poly_to_active + 0.5 * self.poly_contact.first_layer_height active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, @@ -197,9 +192,9 @@ class pnand2(pgate.pgate): self.inputB_yoffset = self.inputA_yoffset + 2 * self.m3_pitch # # active contact metal to poly contact metal spacing - # active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * contact.poly_contact.second_layer_height + # active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * self.poly_contact.second_layer_height # active_bottom = self.pmos1_inst.by() - # active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * contact.poly_contact.first_layer_height + # active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * self.poly_contact.first_layer_height # active_to_poly_contact2 = active_bottom - self.poly_contact_to_gate - 0.5 * self.route_layer_width # self.inputB_yoffset = min(active_contact_to_poly_contact, # active_to_poly_contact, @@ -219,7 +214,7 @@ class pnand2(pgate.pgate): """ Route the Z output """ # One routing track layer below the PMOS contacts - route_layer_offset = 0.5 * contact.poly_contact.second_layer_height + self.route_layer_space + route_layer_offset = 0.5 * self.poly_contact.second_layer_height + self.route_layer_space self.output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset @@ -301,12 +296,12 @@ class pnand2(pgate.pgate): Input inverted by this stage. """ parasitic_delay = 2 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -317,28 +312,26 @@ class pnand2(pgate.pgate): def is_non_inverting(self): """Return input to output polarity for module""" - return False - + def get_on_resistance(self): """On resistance of pnand, defined by stacked NMOS""" is_nchannel = True stack = 2 is_cell = False - return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell) - + return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell) + def get_input_capacitance(self): """Input cap of input, passes width of gates to gate cap function""" - return self.gate_c(self.nmos_width+self.pmos_width) + return self.gate_c(self.nmos_width+self.pmos_width) def get_intrinsic_capacitance(self): """Get the drain capacitances of the TXs in the gate.""" nmos_stack = 2 - nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults, + nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults, nmos_stack, self.tx_mults) - pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, + pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, 1, - self.tx_mults) - return nmos_drain_c + pmos_drain_c - \ No newline at end of file + self.tx_mults) + return nmos_drain_c + pmos_drain_c diff --git a/compiler/pgates/pnand3.py b/compiler/modules/pnand3.py similarity index 93% rename from compiler/pgates/pnand3.py rename to compiler/modules/pnand3.py index b59d0064..31a1b400 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/modules/pnand3.py @@ -5,17 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import pgate +from .pgate import * import debug from tech import drc, parameter, spice -from vector import vector -import logical_effort +from base import vector +from base import logical_effort from sram_factory import factory -import contact from tech import cell_properties as cell_props -class pnand3(pgate.pgate): +class pnand3(pgate): """ This module generates gds of a parametrically sized 2-input nand. This model use ptx to generate a 2-input nand within a cetrain height. @@ -82,7 +81,6 @@ class pnand3(pgate.pgate): tx_type="nmos", add_source_contact="active", add_drain_contact="active") - self.add_mod(self.nmos_center) self.nmos_right = factory.create(module_type="ptx", width=self.nmos_width, @@ -90,7 +88,6 @@ class pnand3(pgate.pgate): tx_type="nmos", add_source_contact="active", add_drain_contact=self.route_layer) - self.add_mod(self.nmos_right) self.nmos_left = factory.create(module_type="ptx", width=self.nmos_width, @@ -98,7 +95,6 @@ class pnand3(pgate.pgate): tx_type="nmos", add_source_contact=self.route_layer, add_drain_contact="active") - self.add_mod(self.nmos_left) self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, @@ -106,7 +102,6 @@ class pnand3(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_left) self.pmos_center = factory.create(module_type="ptx", width=self.pmos_width, @@ -114,7 +109,6 @@ class pnand3(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_center) self.pmos_right = factory.create(module_type="ptx", width=self.pmos_width, @@ -122,7 +116,6 @@ class pnand3(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_right) def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ @@ -210,17 +203,17 @@ class pnand3(pgate.pgate): """ Route the A and B and C inputs """ # We can use this pitch because the contacts and overlap won't be adjacent - non_contact_pitch = 0.5 * self.m1_width + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + non_contact_pitch = 0.5 * self.m1_width + self.m1_space + 0.5 * self.poly_contact.second_layer_height pmos_drain_bottom = self.pmos1_inst.get_pin("D").by() self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space bottom_pin = self.nmos1_inst.get_pin("D") # active contact metal to poly contact metal spacing - active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * self.poly_contact.second_layer_height # active diffusion to poly contact spacing # doesn't use nmos uy because that is calculated using offset + poly height active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height - active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + active_to_poly_contact = active_top + self.poly_to_active + 0.5 * self.poly_contact.first_layer_height active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, @@ -334,12 +327,12 @@ class pnand3(pgate.pgate): Input inverted by this stage. """ parasitic_delay = 3 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -350,27 +343,26 @@ class pnand3(pgate.pgate): def is_non_inverting(self): """Return input to output polarity for module""" - return False - + def get_on_resistance(self): """On resistance of pnand, defined by stacked NMOS""" is_nchannel = True stack = 3 is_cell = False - return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell) - + return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell) + def get_input_capacitance(self): """Input cap of input, passes width of gates to gate cap function""" - return self.gate_c(self.nmos_width+self.pmos_width) - + return self.gate_c(self.nmos_width+self.pmos_width) + def get_intrinsic_capacitance(self): """Get the drain capacitances of the TXs in the gate.""" nmos_stack = 3 - nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults, + nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults, nmos_stack, self.tx_mults) - pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, + pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, 1, - self.tx_mults) - return nmos_drain_c + pmos_drain_c \ No newline at end of file + self.tx_mults) + return nmos_drain_c + pmos_drain_c diff --git a/compiler/pgates/pnand4.py b/compiler/modules/pnand4.py similarity index 94% rename from compiler/pgates/pnand4.py rename to compiler/modules/pnand4.py index 80607331..906cb0e8 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/modules/pnand4.py @@ -5,17 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import pgate +from .pgate import * import debug from tech import drc, parameter, spice -from vector import vector -import logical_effort +from base import vector +from base import logical_effort from sram_factory import factory -import contact from tech import cell_properties as cell_props -class pnand4(pgate.pgate): +class pnand4(pgate): """ This module generates gds of a parametrically sized 4-input nand. This model use ptx to generate a 4-input nand within a cetrain height. @@ -82,7 +81,6 @@ class pnand4(pgate.pgate): tx_type="nmos", add_source_contact="active", add_drain_contact="active") - self.add_mod(self.nmos_center) self.nmos_right = factory.create(module_type="ptx", width=self.nmos_width, @@ -90,7 +88,6 @@ class pnand4(pgate.pgate): tx_type="nmos", add_source_contact="active", add_drain_contact=self.route_layer) - self.add_mod(self.nmos_right) self.nmos_left = factory.create(module_type="ptx", width=self.nmos_width, @@ -98,7 +95,6 @@ class pnand4(pgate.pgate): tx_type="nmos", add_source_contact=self.route_layer, add_drain_contact="active") - self.add_mod(self.nmos_left) self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, @@ -106,7 +102,6 @@ class pnand4(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_left) self.pmos_center = factory.create(module_type="ptx", width=self.pmos_width, @@ -114,7 +109,6 @@ class pnand4(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_center) self.pmos_right = factory.create(module_type="ptx", width=self.pmos_width, @@ -122,7 +116,6 @@ class pnand4(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.pmos_right) def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ @@ -231,11 +224,11 @@ class pnand4(pgate.pgate): bottom_pin = self.nmos1_inst.get_pin("D") # active contact metal to poly contact metal spacing - active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * self.poly_contact.second_layer_height # active diffusion to poly contact spacing # doesn't use nmos uy because that is calculated using offset + poly height active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height - active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + active_to_poly_contact = active_top + self.poly_to_active + 0.5 * self.poly_contact.first_layer_height active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width self.inputA_yoffset = max(active_contact_to_poly_contact, active_to_poly_contact, @@ -356,12 +349,12 @@ class pnand4(pgate.pgate): Input inverted by this stage. """ parasitic_delay = 3 - return logical_effort.logical_effort(self.name, - self.size, - self.input_load(), - cout, - parasitic_delay, - not inp_is_rise) + return logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) def build_graph(self, graph, inst_name, port_nets): """ @@ -390,4 +383,4 @@ class pnand4(pgate.pgate): pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults, 1, self.tx_mults) - return nmos_drain_c + pmos_drain_c \ No newline at end of file + return nmos_drain_c + pmos_drain_c diff --git a/compiler/pgates/pnor2.py b/compiler/modules/pnor2.py similarity index 98% rename from compiler/pgates/pnor2.py rename to compiler/modules/pnor2.py index d59f28fe..35df000f 100644 --- a/compiler/pgates/pnor2.py +++ b/compiler/modules/pnor2.py @@ -5,15 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import pgate +from .pgate import * import debug from tech import drc, parameter, spice -from vector import vector +from base import vector from sram_factory import factory from tech import cell_properties as cell_props -class pnor2(pgate.pgate): +class pnor2(pgate): """ This module generates gds of a parametrically sized 2-input nor. This model use ptx to generate a 2-input nor within a cetrain height. @@ -77,7 +77,6 @@ class pnor2(pgate.pgate): tx_type="nmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.nmos_left) self.nmos_right = factory.create(module_type="ptx", width=self.nmos_width, @@ -85,7 +84,6 @@ class pnor2(pgate.pgate): tx_type="nmos", add_source_contact=self.route_layer, add_drain_contact=self.route_layer) - self.add_mod(self.nmos_right) self.pmos_left = factory.create(module_type="ptx", width=self.pmos_width, @@ -93,7 +91,6 @@ class pnor2(pgate.pgate): tx_type="pmos", add_source_contact=self.route_layer, add_drain_contact="active") - self.add_mod(self.pmos_left) self.pmos_right = factory.create(module_type="ptx", width=self.pmos_width, @@ -101,7 +98,6 @@ class pnor2(pgate.pgate): tx_type="pmos", add_source_contact="active", add_drain_contact=self.route_layer) - self.add_mod(self.pmos_right) def setup_layout_constants(self): """ Pre-compute some handy layout parameters. """ diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 0f456a2d..26dc3405 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -5,15 +5,15 @@ # from math import log, ceil import debug -import design +from base import design from sram_factory import factory -from vector import vector +from base import vector from tech import layer, drc from globals import OPTS from tech import layer_properties as layer_props -class port_address(design.design): +class port_address(design): """ Create the address port (row decoder and wordline driver).. """ @@ -76,16 +76,19 @@ class port_address(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]: - self.copy_power_pins(inst, "vdd") - self.copy_power_pins(inst, "gnd") + if layer_props.wordline_driver.vertical_supply: + self.copy_layout_pin(self.rbl_driver_inst, "vdd") + else: + rbl_pos = self.rbl_driver_inst.get_pin("vdd").rc() + self.add_power_pin("vdd", rbl_pos) + self.add_path("m4", [rbl_pos, self.wordline_driver_array_inst.get_pins("vdd")[0].rc()]) - for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"): - if layer_props.port_address.supply_offset: - self.copy_power_pin(rbl_vdd_pin) - else: - self.copy_power_pin(rbl_vdd_pin, loc=rbl_vdd_pin.lc()) + self.copy_layout_pin(self.wordline_driver_array_inst, "vdd") + self.copy_layout_pin(self.wordline_driver_array_inst, "gnd") + self.copy_layout_pin(self.row_decoder_inst, "vdd") + self.copy_layout_pin(self.row_decoder_inst, "gnd") + # Also connect the B input of the RBL and_dec to vdd if OPTS.local_array_size == 0: rbl_b_pin = self.rbl_driver_inst.get_pin("B") @@ -145,12 +148,10 @@ class port_address(design.design): self.row_decoder = factory.create(module_type="decoder", num_outputs=self.num_rows) - self.add_mod(self.row_decoder) self.wordline_driver_array = factory.create(module_type="wordline_driver_array", rows=self.num_rows, cols=self.num_cols) - self.add_mod(self.wordline_driver_array) local_array_size = OPTS.local_array_size if local_array_size > 0: @@ -174,8 +175,6 @@ class port_address(design.design): size=driver_size, height=b.height) - self.add_mod(self.rbl_driver) - def create_row_decoder(self): """ Create the hierarchical row decoder """ @@ -232,17 +231,15 @@ class port_address(design.design): wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0) self.wordline_driver_array_inst.place(wordline_driver_array_offset) - # The wordline driver also had an extra gap on the right, so use this offset - well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - x_offset = self.wordline_driver_array_inst.rx() - well_gap - self.rbl_driver.width - + # This m4_pitch corresponds to the offset space for jog routing in the + # wordline_driver_array + rbl_driver_offset = wordline_driver_array_offset + vector(2 * self.m4_pitch, 0) + if self.port == 0: - rbl_driver_offset = vector(x_offset, - 0) self.rbl_driver_inst.place(rbl_driver_offset, "MX") else: - rbl_driver_offset = vector(x_offset, - self.wordline_driver_array.height) + rbl_driver_offset += vector(0, + self.wordline_driver_array.height) self.rbl_driver_inst.place(rbl_driver_offset) # Pass this up diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 01ddad8a..a8f5300f 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -5,17 +5,17 @@ # from tech import drc import debug -import design +from base import design import math from sram_factory import factory from collections import namedtuple -from vector import vector +from base import vector from globals import OPTS from tech import cell_properties from tech import layer_properties as layer_props -class port_data(design.design): +class port_data(design): """ Create the data port (column mux, sense amps, write driver, etc.) for the given port number. Port 0 always has the RBL on the left while port 1 is on the right. @@ -29,7 +29,7 @@ class port_data(design.design): self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) else: self.num_wmasks = 0 - + if num_spare_cols is not None: self.num_spare_cols = num_spare_cols + self.num_spare_cols if self.num_spare_cols is None: @@ -215,7 +215,6 @@ class port_data(design.design): bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port], column_offset=self.port - 1) - self.add_mod(self.precharge_array) if self.port in self.read_ports: # RBLs don't get a sense amp @@ -224,7 +223,6 @@ class port_data(design.design): offsets=self.bit_offsets, words_per_row=self.words_per_row, num_spare_cols=self.num_spare_cols) - self.add_mod(self.sense_amp_array) else: self.sense_amp_array = None @@ -236,7 +234,6 @@ class port_data(design.design): offsets=self.bit_offsets, bitcell_bl=self.bl_names[self.port], bitcell_br=self.br_names[self.port]) - self.add_mod(self.column_mux_array) else: self.column_mux_array = None @@ -248,7 +245,6 @@ class port_data(design.design): offsets=self.bit_offsets, write_size=self.write_size, num_spare_cols=self.num_spare_cols) - self.add_mod(self.write_driver_array) if self.write_size is not None: # RBLs don't get a write mask self.write_mask_and_array = factory.create(module_type="write_mask_and_array", @@ -256,7 +252,6 @@ class port_data(design.design): offsets=self.bit_offsets, word_size=self.word_size, write_size=self.write_size) - self.add_mod(self.write_mask_and_array) else: self.write_mask_and_array = None @@ -516,9 +511,6 @@ class port_data(design.design): wdriver_inst = self.write_driver_array_inst for bit in range(self.num_wmasks): - # Bring write mask AND array output pin to port data level - self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) - wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit)) wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit)) @@ -858,10 +850,10 @@ class port_data(design.design): """ if self.column_mux_array: self.column_mux_array.graph_exclude_columns(column_include_num) - + def graph_clear_column_mux(self): """ Clear mux exclusions to allow different bit tests. """ if self.column_mux_array: - self.column_mux_array.init_graph_params() + self.column_mux_array.init_graph_params() diff --git a/compiler/pgates/precharge.py b/compiler/modules/precharge.py similarity index 88% rename from compiler/pgates/precharge.py rename to compiler/modules/precharge.py index 951fe834..4a1267e5 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/modules/precharge.py @@ -5,18 +5,17 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact -import design +from base import design import debug -from pgate import pgate +from .pgate import * from tech import parameter, drc -from vector import vector +from base import vector from globals import OPTS from sram_factory import factory from tech import cell_properties as cell_props -class precharge(design.design): +class precharge(design): """ Creates a single precharge cell This module implements the precharge bitline cell used in the design. @@ -71,7 +70,7 @@ class precharge(design.design): self.connect_poly() self.route_en() self.place_nwell_and_contact() - self.route_vdd_rail() + self.route_supplies() self.route_bitlines() self.connect_to_bitlines() self.add_boundary() @@ -90,35 +89,19 @@ class precharge(design.design): width=self.ptx_width, mults=self.ptx_mults, tx_type="pmos") - self.add_mod(self.pmos) - def route_vdd_rail(self): + def route_supplies(self): """ Adds a vdd rail at the top of the cell """ - # Adds the rail across the width of the cell - vdd_position = vector(0.5 * self.width, self.height) - layer_width = drc("minwidth_" + self.en_layer) - self.add_rect_center(layer=self.en_layer, - offset=vdd_position, - width=self.width, - height=layer_width) - pmos_pin = self.upper_pmos2_inst.get_pin("S") + pmos_pos = pmos_pin.center() + self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) - # center of vdd rail - pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) - self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) - - self.add_power_pin("vdd", - self.well_contact_pos, - directions=("V", "V")) - - self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.en_layer, - offset=pmos_pin.center(), - directions=("V", "V")) + self.add_layout_pin_rect_center(text="vdd", + layer=pmos_pin.layer, + offset=self.well_contact_pos) def create_ptx(self): """ @@ -194,7 +177,7 @@ class precharge(design.design): pin_offset = self.lower_pmos_inst.get_pin("G").lr() # This is an extra space down for some techs with contact to active spacing contact_space = max(self.poly_space, - self.poly_contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height + self.poly_contact_to_gate) + 0.5 * self.poly_contact.first_layer_height offset = pin_offset - vector(0, contact_space) self.add_via_stack_center(from_layer="poly", to_layer=self.en_layer, @@ -214,7 +197,7 @@ class precharge(design.design): # adds the contact from active to metal1 offset_height = self.upper_pmos1_inst.uy() + \ - contact.active_contact.height + \ + self.active_contact.height + \ self.nwell_extend_active self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \ vector(0, offset_height) @@ -226,7 +209,7 @@ class precharge(design.design): to_layer=self.bitline_layer, offset=self.well_contact_pos) - self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space + self.height = self.well_contact_pos.y + self.active_contact.height + self.m1_space # nwell should span the whole design since it is pmos only self.add_rect(layer="nwell", @@ -305,4 +288,3 @@ class precharge(design.design): self.add_path(self.bitline_layer, [left_pos, right_pos], width=pmos_pin.height()) - diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index ed19b387..3c7ab681 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -5,14 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design import debug -from vector import vector +from base import vector from sram_factory import factory from globals import OPTS -class precharge_array(design.design): +class precharge_array(design): """ Dynamically generated precharge array of all bitlines. Cols is number of bit line columns, height is the height of the bit-cell array. @@ -68,6 +68,7 @@ class precharge_array(design.design): self.height = self.pc_cell.height self.add_layout_pins() + self.route_supplies() self.add_boundary() self.DRC_LVS() @@ -76,31 +77,26 @@ class precharge_array(design.design): size=self.size, bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) - - self.add_mod(self.pc_cell) + self.cell = factory.create(module_type=OPTS.bitcell) def add_layout_pins(self): en_pin = self.pc_cell.get_pin("en_bar") - start_offset = en_pin.lc().scale(0, 1) - end_offset = start_offset + vector(self.width, 0) - self.add_layout_pin_segment_center(text="en_bar", - layer=self.en_bar_layer, - start=start_offset, - end=end_offset) - + self.route_horizontal_pins("en_bar", layer=self.en_bar_layer) for inst in self.local_insts: self.add_via_stack_center(from_layer=en_pin.layer, to_layer=self.en_bar_layer, offset=inst.get_pin("en_bar").center()) - self.copy_layout_pin(inst, "vdd") for i in range(len(self.local_insts)): inst = self.local_insts[i] self.copy_layout_pin(inst, "bl", "bl_{0}".format(i)) self.copy_layout_pin(inst, "br", "br_{0}".format(i)) + def route_supplies(self): + self.route_horizontal_pins("vdd") + def create_insts(self): """Creates a precharge array by horizontally tiling the precharge cell""" self.local_insts = [] @@ -130,5 +126,3 @@ class precharge_array(design.design): offset = vector(tempx, 0) self.local_insts[i].place(offset=offset, mirror=mirror) - - diff --git a/compiler/pgates/ptristate_inv.py b/compiler/modules/ptristate_inv.py similarity index 89% rename from compiler/pgates/ptristate_inv.py rename to compiler/modules/ptristate_inv.py index 8cac43c0..583b68ea 100644 --- a/compiler/pgates/ptristate_inv.py +++ b/compiler/modules/ptristate_inv.py @@ -5,15 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import contact -import pgate +from .pgate import * import debug from tech import drc, parameter, spice -from vector import vector +from base import vector from sram_factory import factory -class ptristate_inv(pgate.pgate): +class ptristate_inv(pgate): """ ptristate generates gds of a parametrically sized tristate inverter. There is some flexibility in the size, but we do not allow multiple fingers @@ -85,13 +84,11 @@ class ptristate_inv(pgate.pgate): width=self.nmos_width, mults=1, tx_type="nmos") - self.add_mod(self.nmos) self.pmos = factory.create(module_type="ptx", width=self.pmos_width, mults=1, tx_type="pmos") - self.add_mod(self.pmos) def route_supply_rails(self): """ Add vdd/gnd rails to the top and bottom. """ @@ -133,8 +130,8 @@ class ptristate_inv(pgate.pgate): """ pmos_yoff = self.height - self.pmos.active_height \ - - self.top_bottom_space - 0.5 * contact.active_contact.height - nmos_yoff = self.top_bottom_space + 0.5 * contact.active_contact.height + - self.top_bottom_space - 0.5 * self.active_contact.height + nmos_yoff = self.top_bottom_space + 0.5 * self.active_contact.height # Tristate transistors pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) @@ -190,16 +187,16 @@ class ptristate_inv(pgate.pgate): drain_pos = self.nmos1_inst.get_pin("S").center() vdd_pos = self.get_pin("vdd").center() - self.nwell_contact = self.add_via_center(layers=layer_stack, - offset=vector(drain_pos.x, vdd_pos.y), - implant_type="n", - well_type="n") + self.add_via_center(layers=layer_stack, + offset=vector(drain_pos.x, vdd_pos.y), + implant_type="n", + well_type="n") gnd_pos = self.get_pin("gnd").center() - self.pwell_contact = self.add_via_center(layers=layer_stack, - offset=vector(drain_pos.x, gnd_pos.y), - implant_type="p", - well_type="p") + self.add_via_center(layers=layer_stack, + offset=vector(drain_pos.x, gnd_pos.y), + implant_type="p", + well_type="p") def connect_rails(self): """ Connect the nmos and pmos to its respective power rails """ diff --git a/compiler/pgates/ptx.py b/compiler/modules/ptx.py similarity index 97% rename from compiler/pgates/ptx.py rename to compiler/modules/ptx.py index 77950b82..d1bd13c8 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/modules/ptx.py @@ -5,18 +5,17 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design import debug +from base import design +from base import logical_effort +from base import vector from tech import layer, drc, spice -from vector import vector from sram_factory import factory -import contact -import logical_effort from globals import OPTS from tech import cell_properties as cell_props -class ptx(design.design): +class ptx(design): """ This module generates gds and spice of a parametrically NMOS or PMOS sized transistor. Pins are accessed as D, G, S, B. Width is @@ -156,13 +155,12 @@ class ptx(design.design): # self.tx_width, # drc("minwidth_poly")) # TEMP FIX: Use old device names if using Calibre. - + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult=1".format("nshort" if self.tx_type == "nmos" else "pshort", self.mults, self.tx_width, drc("minwidth_poly")) elif cell_props.ptx.model_is_subckt: - # sky130 requires mult parameter too self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type], self.mults, self.tx_width, @@ -199,12 +197,12 @@ class ptx(design.design): # This is the extra poly spacing due to the poly contact to poly contact pitch # of contacted gates - extra_poly_contact_width = contact.poly_contact.width - self.poly_width + extra_poly_contact_width = self.poly_contact.width - self.poly_width # This is the spacing between S/D contacts # This is the spacing between the poly gates self.min_poly_pitch = self.poly_space + self.poly_width - self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width + self.contacted_poly_pitch = self.poly_space + self.poly_contact.width self.contact_pitch = 2 * self.active_contact_to_gate + self.poly_width + self.contact_width self.poly_pitch = max(self.min_poly_pitch, self.contacted_poly_pitch, @@ -488,11 +486,11 @@ class ptx(design.design): # FIXME: Using the same definition as the pinv.py. parasitic_delay = 1 size = self.mults * self.tx_width / drc("minwidth_tx") - return logical_effort.logical_effort(self.name, - size, - self.input_load(), - cout, - parasitic_delay) + return logical_effort(self.name, + size, + self.input_load(), + cout, + parasitic_delay) def input_load(self): """ @@ -550,7 +548,7 @@ class ptx(design.design): def is_non_inverting(self): """Return input to output polarity for module""" - + return True def get_on_resistance(self): @@ -558,14 +556,14 @@ class ptx(design.design): is_nchannel = (self.tx_type == "nmos") stack = 1 is_cell = False - return self.tr_r_on(self.tx_width, is_nchannel, stack, is_cell) - + return self.tr_r_on(self.tx_width, is_nchannel, stack, is_cell) + def get_input_capacitance(self): """Input cap of input, passes width of gates to gate cap function""" - return self.gate_c(self.tx_width) + return self.gate_c(self.tx_width) def get_intrinsic_capacitance(self): """Get the drain capacitances of the TXs in the gate.""" - return self.drain_c_(self.tx_width*self.mults, + return self.drain_c_(self.tx_width*self.mults, 1, self.mults) diff --git a/compiler/pgates/pwrite_driver.py b/compiler/modules/pwrite_driver.py similarity index 98% rename from compiler/pgates/pwrite_driver.py rename to compiler/modules/pwrite_driver.py index 2b4a3679..9af6c78f 100644 --- a/compiler/pgates/pwrite_driver.py +++ b/compiler/modules/pwrite_driver.py @@ -5,15 +5,15 @@ #(acting for and on behalf of Oklahoma State University) #All rights reserved. # -import design +from base import design from tech import parameter import debug -from vector import vector +from base import vector from globals import OPTS from sram_factory import factory -class pwrite_driver(design.design): +class pwrite_driver(design): """ The pwrite_driver is two tristate inverters that drive the bitlines. The data input is first inverted before one tristate. @@ -66,19 +66,16 @@ class pwrite_driver(design.design): # Tristate inverter self.tri = factory.create(module_type="ptristate_inv", height="min") - self.add_mod(self.tri) debug.check(self.tri.width2: self.msb_decoder = self.bank.decoder.pre2_4 - self.add_mod(self.msb_decoder) def add_modules(self): self.bitcell = factory.create(module_type=OPTS.bitcell) @@ -480,30 +475,24 @@ class sram_base(design, verilog, lef): # Create the bank module (up to four are instantiated) self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") - self.add_mod(self.bank) self.num_spare_cols = self.bank.num_spare_cols # Create the address and control flops (but not the clk) self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1) - self.add_mod(self.row_addr_dff) if self.col_addr_size > 0: self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size) - self.add_mod(self.col_addr_dff) else: self.col_addr_dff = None self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size + self.num_spare_cols) - self.add_mod(self.data_dff) if self.write_size: self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) - self.add_mod(self.wmask_dff) if self.num_spare_cols: self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols) - self.add_mod(self.spare_wen_dff) # Create bank decoder if(self.num_banks > 1): @@ -511,34 +500,31 @@ class sram_base(design, verilog, lef): self.bank_count = 0 - c = reload(__import__(OPTS.control_logic)) + c = importlib.import_module("modules." + OPTS.control_logic) self.mod_control_logic = getattr(c, OPTS.control_logic) # Create the control logic module for each port type - if len(self.readwrite_ports)>0: + if len(self.readwrite_ports) > 0: self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, spare_columns=self.num_spare_cols, sram=self, port_type="rw") - self.add_mod(self.control_logic_rw) - if len(self.writeonly_ports)>0: + if len(self.writeonly_ports) > 0: self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, spare_columns=self.num_spare_cols, sram=self, port_type="w") - self.add_mod(self.control_logic_w) - if len(self.readonly_ports)>0: + if len(self.readonly_ports) > 0: self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, word_size=self.word_size, spare_columns=self.num_spare_cols, sram=self, port_type="r") - self.add_mod(self.control_logic_r) def create_bank(self, bank_num): """ Create a bank """ @@ -702,7 +688,10 @@ class sram_base(design, verilog, lef): inputs = [] outputs = [] for bit in range(self.num_spare_cols): - inputs.append("spare_wen{}[{}]".format(port, bit)) + if self.num_spare_cols == 1: + inputs.append("spare_wen{0}".format(port)) + else: + inputs.append("spare_wen{0}[{1}]".format(port, bit)) outputs.append("bank_spare_wen{}_{}".format(port, bit)) self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) @@ -779,13 +768,13 @@ class sram_base(design, verilog, lef): Clears the bit exclusions """ self.bank.clear_exclude_bits() - + def graph_exclude_column_mux(self, column_include_num, port): """ Excludes all columns muxes unrelated to the target bit being simulated. """ self.bank.graph_exclude_column_mux(column_include_num, port) - + def graph_clear_column_mux(self, port): """ Clear mux exclusions to allow different bit tests. diff --git a/compiler/sram/sram_config.py b/compiler/modules/sram_config.py similarity index 100% rename from compiler/sram/sram_config.py rename to compiler/modules/sram_config.py diff --git a/compiler/custom/tri_gate.py b/compiler/modules/tri_gate.py similarity index 96% rename from compiler/custom/tri_gate.py rename to compiler/modules/tri_gate.py index f0c0afeb..c5d65d57 100644 --- a/compiler/custom/tri_gate.py +++ b/compiler/modules/tri_gate.py @@ -6,11 +6,11 @@ # All rights reserved. # import debug -import design +from base import design from tech import spice -class tri_gate(design.design): +class tri_gate(design): """ This module implements the tri gate cell used in the design forS bit-line isolation. It is a hand-made cell, so the layout and diff --git a/compiler/modules/tri_gate_array.py b/compiler/modules/tri_gate_array.py index 106c9169..984d8039 100644 --- a/compiler/modules/tri_gate_array.py +++ b/compiler/modules/tri_gate_array.py @@ -7,12 +7,12 @@ # import debug from tech import drc -import design -from vector import vector +from base import design +from base import vector from sram_factory import factory from globals import OPTS -class tri_gate_array(design.design): +class tri_gate_array(design): """ Dynamically generated tri gate array of all bitlines. words_per_row """ @@ -46,7 +46,6 @@ class tri_gate_array(design.design): def add_modules(self): self.tri = factory.create(module_type="tri_gate") - self.add_mod(self.tri) def add_pins(self): """create the name of pins depend on the word size""" @@ -120,4 +119,4 @@ class tri_gate_array(design.design): layer="m1", offset=enbar_pin.ll().scale(0, 1), width=width, - height=drc("minwidth_m1")) \ No newline at end of file + height=drc("minwidth_m1")) diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py index 31730980..d624d5db 100644 --- a/compiler/modules/wordline_buffer_array.py +++ b/compiler/modules/wordline_buffer_array.py @@ -6,21 +6,21 @@ # All rights reserved. # import debug -import design +from base import design from tech import layer -from vector import vector +from base import vector from sram_factory import factory from globals import OPTS from tech import layer_properties as layer_props -class wordline_buffer_array(design.design): +class wordline_buffer_array(design): """ Creates a Wordline Buffer/Inverter array """ def __init__(self, name, rows, cols): - design.design.__init__(self, name) + design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -43,8 +43,11 @@ class wordline_buffer_array(design.design): self.route_layer = "m1" self.place_drivers() self.route_layout() - self.route_vdd_gnd() - self.offset_all_coordinates() + self.route_supplies() + + # Don't offset these because some cells use standard cell style drivers + #self.offset_all_coordinates() + self.add_boundary() self.DRC_LVS() @@ -64,39 +67,18 @@ class wordline_buffer_array(design.design): self.wl_driver = factory.create(module_type="inv_dec", size=self.cols, height=b.height) - self.add_mod(self.wl_driver) - def route_vdd_gnd(self): + def route_supplies(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ if layer_props.wordline_driver.vertical_supply: - for name in ["vdd", "gnd"]: - supply_pins = self.wld_inst[0].get_pins(name) - for pin in supply_pins: - self.add_layout_pin_segment_center(text=name, - layer=pin.layer, - start=pin.bc(), - end=vector(pin.cx(), self.height)) + self.route_vertical_pins("vdd", self.wld_inst) + self.route_vertical_pins("gnd", self.wld_inst) else: - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.wld_inst[0].rx()] - for num in range(self.rows): - # this will result in duplicate polygons for rails, but who cares - - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.wl_driver.height, - num) - # Route both supplies - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) + self.route_vertical_pins("vdd", self.wld_inst, xside="rx",) + self.route_vertical_pins("gnd", self.wld_inst, xside="lx",) def create_drivers(self): self.wld_inst = [] diff --git a/compiler/pgates/wordline_driver.py b/compiler/modules/wordline_driver.py similarity index 97% rename from compiler/pgates/wordline_driver.py rename to compiler/modules/wordline_driver.py index 46b44f9b..e8fd8901 100644 --- a/compiler/pgates/wordline_driver.py +++ b/compiler/modules/wordline_driver.py @@ -6,15 +6,15 @@ # All rights reserved. # import debug -from vector import vector -import design +from base import vector +from base import design from sram_factory import factory from globals import OPTS from tech import layer from tech import layer_properties as layer_props -class wordline_driver(design.design): +class wordline_driver(design): """ This is an AND (or NAND) with configurable drive strength to drive the wordlines. It is matched to the bitcell height. @@ -62,9 +62,6 @@ class wordline_driver(design.design): size=driver_size, height=self.nand.height) - self.add_mod(self.nand) - self.add_mod(self.driver) - def create_layout(self): self.width = self.nand.width + self.driver.width if "li" in layer: diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index 79906a3a..8a1e100a 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -6,15 +6,15 @@ # All rights reserved. # import debug -import design +from base import design from tech import drc, layer -from vector import vector +from base import vector from sram_factory import factory from globals import OPTS from tech import layer_properties as layer_props -class wordline_driver_array(design.design): +class wordline_driver_array(design): """ Creates a Wordline Driver Generates the wordline-driver to drive the bitcell @@ -42,11 +42,18 @@ class wordline_driver_array(design.design): self.route_layer = "li" else: self.route_layer = "m1" + self.place_drivers() self.route_layout() - self.route_vdd_gnd() - self.offset_x_coordinates() + self.offset_x_coordinates(vector(-2*self.m4_pitch, 0)) + + # Leave a well gap to separate the bitcell array well from this well + well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") + self.width = self.wld_inst[-1].rx() + well_gap + self.height = self.wld_inst[-1].uy() + self.add_boundary() + self.route_supplies() self.DRC_LVS() def add_pins(self): @@ -65,39 +72,18 @@ class wordline_driver_array(design.design): self.wl_driver = factory.create(module_type="wordline_driver", cols=self.cols) - self.add_mod(self.wl_driver) + def route_supplies(self): + """ + Add vertical power rails. + """ - def route_vdd_gnd(self): - """ - Add a pin for each row of vdd/gnd which - are must-connects next level up. - """ if layer_props.wordline_driver.vertical_supply: - for name in ["vdd", "gnd"]: - supply_pins = self.wld_inst[0].get_pins(name) - for pin in supply_pins: - self.add_layout_pin_segment_center(text=name, - layer=pin.layer, - start=pin.bc(), - end=vector(pin.cx(), self.height)) + self.route_vertical_pins("vdd", self.wld_inst) + self.route_vertical_pins("gnd", self.wld_inst) else: - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.wld_inst[0].rx()] - for num in range(self.rows): - # this will result in duplicate polygons for rails, but who cares + self.route_vertical_pins("vdd", self.wld_inst, xside="rx",) + self.route_vertical_pins("gnd", self.wld_inst, xside="lx",) - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.wl_driver.height, - num) - # Route both supplies - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) def create_drivers(self): self.wld_inst = [] @@ -128,21 +114,17 @@ class wordline_driver_array(design.design): self.wld_inst[row].place(offset=and2_offset, mirror=inst_mirror) - # Leave a well gap to separate the bitcell array well from this well - well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - self.width = self.wl_driver.width + well_gap - self.height = self.wl_driver.height * self.rows - def route_layout(self): """ Route all of the signals """ # Wordline enable connection en_pin = self.wld_inst[0].get_pin("B") - en_bottom_pos = vector(en_pin.lx(), 0) - en_pin = self.add_layout_pin(text="en", - layer="m2", - offset=en_bottom_pos, - height=self.height) + en_bottom_pos = vector(en_pin.cx(), 0) + en_top_pos = vector(en_pin.cx(), self.wld_inst[-1].uy()) + en_pin = self.add_layout_pin_segment_center(text="en", + layer="m2", + start=en_bottom_pos, + end=en_top_pos) for row in range(self.rows): and_inst = self.wld_inst[row] diff --git a/compiler/custom/write_driver.py b/compiler/modules/write_driver.py similarity index 96% rename from compiler/custom/write_driver.py rename to compiler/modules/write_driver.py index 6d5c4018..00afa0ee 100644 --- a/compiler/custom/write_driver.py +++ b/compiler/modules/write_driver.py @@ -6,11 +6,11 @@ # All rights reserved. # import debug -import design +from base import design from tech import cell_properties as props -class write_driver(design.design): +class write_driver(design): """ Tristate write driver to be active during write operations only. This module implements the write driver cell used in the design. It diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 07c2ce60..ee3d3c2f 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -5,16 +5,16 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design import debug import math from tech import drc from sram_factory import factory -from vector import vector +from base import vector from globals import OPTS -class write_driver_array(design.design): +class write_driver_array(design): """ Array of tristate drivers to write to the bitlines through the column mux. Dynamically generated write driver array of all bitlines. @@ -69,9 +69,10 @@ class write_driver_array(design.design): def create_layout(self): self.place_write_array() - self.width = self.driver_insts[-1].rx() + self.width = self.local_insts[-1].rx() self.height = self.driver.height self.add_layout_pins() + self.route_supplies() self.add_boundary() self.DRC_LVS() @@ -94,20 +95,19 @@ class write_driver_array(design.design): def add_modules(self): self.driver = factory.create(module_type="write_driver") - self.add_mod(self.driver) # This is just used for measurements, # so don't add the module self.bitcell = factory.create(module_type=OPTS.bitcell) def create_write_array(self): - self.driver_insts = [] + self.local_insts = [] w = 0 windex=0 for i in range(0, self.columns, self.words_per_row): name = "write_driver{}".format(i) index = int(i / self.words_per_row) - self.driver_insts.append(self.add_inst(name=name, + self.local_insts.append(self.add_inst(name=name, mod=self.driver)) if self.write_size: @@ -140,7 +140,7 @@ class write_driver_array(design.design): else: offset = 1 name = "write_driver{}".format(self.columns + i) - self.driver_insts.append(self.add_inst(name=name, + self.local_insts.append(self.add_inst(name=name, mod=self.driver)) self.connect_inst([self.data_name + "_{0}".format(index), @@ -167,7 +167,7 @@ class write_driver_array(design.design): mirror = "" base = vector(xoffset, 0) - self.driver_insts[i].place(offset=base, mirror=mirror) + self.local_insts[i].place(offset=base, mirror=mirror) # place spare write drivers (if spare columns are specified) for i, xoffset in enumerate(self.offsets[self.columns:]): @@ -180,11 +180,11 @@ class write_driver_array(design.design): mirror = "" base = vector(xoffset, 0) - self.driver_insts[index].place(offset=base, mirror=mirror) + self.local_insts[index].place(offset=base, mirror=mirror) def add_layout_pins(self): for i in range(self.word_size + self.num_spare_cols): - inst = self.driver_insts[i] + inst = self.local_insts[i] din_pin = inst.get_pin(inst.mod.din_name) self.add_layout_pin(text=self.data_name + "_{0}".format(i), layer=din_pin.layer, @@ -205,14 +205,9 @@ class write_driver_array(design.design): width=br_pin.width(), height=br_pin.height()) - for n in ["vdd", "gnd"]: - pin_list = self.driver_insts[i].get_pins(n) - for pin in pin_list: - self.copy_power_pin(pin, directions=("V", "V")) - if self.write_size: for bit in range(self.num_wmasks): - inst = self.driver_insts[bit * self.write_size] + inst = self.local_insts[bit * self.write_size] en_pin = inst.get_pin(inst.mod.en_name) # Determine width of wmask modified en_pin with/without col mux wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing) @@ -228,7 +223,7 @@ class write_driver_array(design.design): height=en_pin.height()) for i in range(self.num_spare_cols): - inst = self.driver_insts[self.word_size + i] + inst = self.local_insts[self.word_size + i] en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + self.num_wmasks), layer="m1", @@ -236,9 +231,9 @@ class write_driver_array(design.design): elif self.num_spare_cols and not self.write_size: # shorten enable rail to accomodate those for spare write drivers - left_inst = self.driver_insts[0] + left_inst = self.local_insts[0] left_en_pin = left_inst.get_pin(inst.mod.en_name) - right_inst = self.driver_insts[-self.num_spare_cols - 1] + right_inst = self.local_insts[-self.num_spare_cols - 1] right_en_pin = right_inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(0), layer="m1", @@ -247,16 +242,21 @@ class write_driver_array(design.design): # individual enables for every spare write driver for i in range(self.num_spare_cols): - inst = self.driver_insts[self.word_size + i] + inst = self.local_insts[self.word_size + i] en_pin = inst.get_pin(inst.mod.en_name) self.add_layout_pin(text=self.en_name + "_{0}".format(i + 1), layer="m1", offset=en_pin.lr() + vector(-drc("minwidth_m1"), 0)) else: - inst = self.driver_insts[0] + inst = self.local_insts[0] self.add_layout_pin(text=self.en_name, layer="m1", offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), width=self.width) + def route_supplies(self): + self.route_horizontal_pins("vdd") + self.route_horizontal_pins("gnd") + + diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index 2ccf34a1..f3e7e9bc 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -5,15 +5,15 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import design +from base import design import debug import math from sram_factory import factory -from vector import vector +from base import vector from globals import OPTS -class write_mask_and_array(design.design): +class write_mask_and_array(design): """ Array of AND gates to turn write mask signal on only when w_en is on. The write mask AND array goes between the write driver array and the sense amp array. @@ -63,7 +63,6 @@ class write_mask_and_array(design.design): # Assume stage effort of 3 to compute the size self.and2 = factory.create(module_type="pand2", size=max(self.write_size / 4.0, 1)) - self.add_mod(self.and2) def create_and2_array(self): self.and2_insts = {} @@ -146,7 +145,7 @@ class write_mask_and_array(design.design): self.add_via_stack_center(from_layer=supply_pin.layer, to_layer="m1", offset=supply_pin.center()) - + for supply in ["gnd", "vdd"]: supply_pin = self.and2_insts[0].get_pin(supply) supply_pin_yoffset = supply_pin.cy() @@ -158,4 +157,3 @@ class write_mask_and_array(design.design): to_layer="m1", offset=loc) self.copy_power_pin(supply_pin, loc=loc) - diff --git a/compiler/openram.py b/compiler/openram.py index 089c0f3a..fbbf4466 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -47,7 +47,7 @@ g.print_time("Start", start_time) # Output info about this run g.report_status() -from sram_config import sram_config +from modules import sram_config # Configure the SRAM organization @@ -73,9 +73,9 @@ for path in output_files: debug.print_raw(path) -from sram import sram -s = sram(sram_config=c, - name=OPTS.output_name) +from modules import sram +s = sram(name=OPTS.output_name, + sram_config=c) # Output the files for the resulting SRAM s.save() diff --git a/compiler/options.py b/compiler/options.py index f3c19283..770045b3 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -166,15 +166,12 @@ class options(optparse.Values): keep_temp = False - # Add a prefix of the root cell before every structure in the GDS - # after outputting the GDS2 - uniquify = False - # These are the default modules that can be over-riden bank_select = "bank_select" bitcell_array = "bitcell_array" bitcell = "bitcell" buf_dec = "pbuf" + column_decoder = "column_decoder" column_mux_array = "column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" diff --git a/compiler/router/__init__.py b/compiler/router/__init__.py new file mode 100644 index 00000000..a4496fb0 --- /dev/null +++ b/compiler/router/__init__.py @@ -0,0 +1,5 @@ +from .router import * +from .signal_escape_router import * +from .signal_router import * +from .supply_grid_router import * +from .supply_tree_router import * diff --git a/compiler/router/direction.py b/compiler/router/direction.py index c13abdd3..a7eeb727 100644 --- a/compiler/router/direction.py +++ b/compiler/router/direction.py @@ -6,7 +6,7 @@ # All rights reserved. # from enum import Enum -from vector3d import vector3d +from base.vector3d import vector3d import debug diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 843fb3ed..8fd1de72 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -6,8 +6,8 @@ # All rights reserved. # import debug -from vector3d import vector3d -from grid_cell import grid_cell +from base.vector3d import vector3d +from .grid_cell import grid_cell class grid: @@ -68,6 +68,16 @@ class grid: self.add_map(n) return self.map[n].blocked + def is_inside(self, n): + if not isinstance(n, vector3d): + for item in n: + if self.is_inside(item): + return True + else: + return False + else: + return n.x >= self.ll.x and n.x <= self.ur.x and n.y >= self.ll.y and n.y <= self.ur.y + def set_path(self, n, value=True): if isinstance(n, (list, tuple, set, frozenset)): for item in n: @@ -128,7 +138,7 @@ class grid: """ Side specifies which side. Layer specifies horizontal (0) or vertical (1) - Width specifies how wide the perimter "stripe" should be. + Width specifies how wide the perimeter "stripe" should be. Works from the inside out from the bbox (ll, ur) """ if "ring" in side: diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py index 42be2761..f201a094 100644 --- a/compiler/router/grid_cell.py +++ b/compiler/router/grid_cell.py @@ -20,8 +20,7 @@ class grid_cell: def reset(self): """ - Reset the dynamic info about routing. The pins/blockages are not reset so - that they can be reused. + Reset the dynamic info about routing. """ self.min_cost=-1 self.min_path=None diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py index d47b2677..1c7c576a 100644 --- a/compiler/router/grid_path.py +++ b/compiler/router/grid_path.py @@ -5,10 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from vector3d import vector3d from itertools import tee -from grid import grid -from direction import direction +from base.vector3d import vector3d +from .grid import grid +from .direction import direction class grid_path: diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py index 1085f5c7..0bf954a3 100644 --- a/compiler/router/grid_utils.py +++ b/compiler/router/grid_utils.py @@ -10,8 +10,8 @@ Some utility functions for sets of grid cells. """ import math -from direction import direction -from vector3d import vector3d +from .direction import direction +from base.vector3d import vector3d def increment_set(curset, direct): diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 5e6d6f89..0819d62a 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -5,11 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from direction import direction -from pin_layout import pin_layout -from vector import vector -from vector3d import vector3d import debug +from base.vector import vector +from base.vector3d import vector3d +from base.pin_layout import pin_layout +from .direction import direction class pin_group: @@ -158,7 +158,7 @@ class pin_group: # Now add the right name for pin in new_pin_list: pin.name = self.name - + debug.check(len(new_pin_list) > 0, "Did not find any enclosures.") @@ -424,7 +424,7 @@ class pin_group: debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.") common_blockages = self.router.get_blocked_grids() & self.grids - + # Start with the ll and make the widest row row = [ll] # Move in dir1 while we can @@ -641,7 +641,7 @@ class pin_group: # way than blockages. blockages = sufficient | insufficient | blockage_in_tracks self.blockages.update(blockages) - + # If we have a blockage, we must remove the grids # Remember, this excludes the pin blockages already blocked_grids = self.router.get_blocked_grids() diff --git a/compiler/router/router.py b/compiler/router/router.py index f82a6128..756047e9 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -8,18 +8,18 @@ import itertools import math -import gdsMill +from datetime import datetime +from gdsMill import gdsMill +import debug +from globals import OPTS, print_time from tech import drc, GDS from tech import layer as techlayer -import debug -from router_tech import router_tech -from pin_layout import pin_layout -from pin_group import pin_group -from vector import vector -from vector3d import vector3d -from globals import OPTS, print_time -import grid_utils -from datetime import datetime +from base.vector import vector +from base.vector3d import vector3d +from base.pin_layout import pin_layout +from .router_tech import router_tech +from .pin_group import pin_group +from . import grid_utils class router(router_tech): @@ -92,12 +92,19 @@ class router(router_tech): def get_bbox(self): return self.bbox - def create_routing_grid(self, router_type): + def create_routing_grid(self, router_type=None): """ - Create a sprase routing grid with A* expansion functions. + Create (or recreate) a sprase routing grid with A* expansion functions. """ + debug.check(router_type or hasattr(self, "router_type"), "Must specify a routing grid type.") + self.init_bbox(self.bbox, self.margin) - self.rg = router_type(self.ll, self.ur, self.track_width) + + if router_type: + self.router_type = router_type + self.rg = router_type(self.ll, self.ur, self.track_width) + else: + self.rg = self.router_type(self.ll, self.ur, self.track_width) def clear_pins(self): """ @@ -222,9 +229,9 @@ class router(router_tech): # Enclose the continguous grid units in a metal # rectangle to fix some DRCs - start_time = datetime.now() - self.enclose_pins() - print_time("Enclosing pins", datetime.now(), start_time, 4) + #start_time = datetime.now() + #self.enclose_pins() + #print_time("Enclosing pins", datetime.now(), start_time, 4) # MRG: Removing this code for now. The later compute enclosure code # assumes that all pins are touching and this may produce sets of pins @@ -368,9 +375,10 @@ class router(router_tech): # This is just a virtual function pass - def prepare_blockages(self): + def prepare_blockages(self, src=None, dest=None): """ Reset and add all of the blockages in the design. + Skip adding blockages from src and dest component if specified as a tuple of name,component. """ debug.info(3, "Preparing blockages.") @@ -393,8 +401,14 @@ class router(router_tech): # Now go and block all of the blockages due to pin shapes. # Some of these will get unblocked later if they are the source/target. for name in self.pin_groups: - # This should be a superset of the grids... - blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} + blockage_grids = [] + for component_idx, component in enumerate(self.pin_groups[name]): + # Skip adding source or dest blockages + if src and src[0] == name and src[1] == component_idx: + continue + if dest and dest[0] == name and dest[1] == component_idx: + continue + blockage_grids.extend(component.blockages) self.set_blockages(blockage_grids, True) # If we have paths that were recently routed, add them as blockages as well. @@ -560,20 +574,18 @@ class router(router_tech): debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur)) # scale the size bigger to include neaby tracks - ll = ll.scale(self.track_factor).floor() - ur = ur.scale(self.track_factor).ceil() + ll_scaled = ll.scale(self.track_factor).floor() + ur_scaled = ur.scale(self.track_factor).ceil() # Keep tabs on tracks with sufficient and insufficient overlap sufficient_list = set() insufficient_list = set() zindex = self.get_zindex(pin.lpp) - for x in range(int(ll[0]) - expansion, int(ur[0]) + 1 + expansion): - for y in range(int(ll[1] - expansion), int(ur[1]) + 1 + expansion): - (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, - vector3d(x, - y, - zindex)) + for x in range(int(ll_scaled[0]) - expansion, int(ur_scaled[0]) + 1 + expansion): + for y in range(int(ll_scaled[1] - expansion), int(ur_scaled[1]) + 1 + expansion): + cur_grid = vector3d(x, y, zindex) + (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, cur_grid) if full_overlap: sufficient_list.update([full_overlap]) if partial_overlap: @@ -656,6 +668,43 @@ class router(router_tech): return set([best_coord]) + def break_on_grids(self, tracks, xvals, yvals): + track_list = [] + for x in xvals: + for y in yvals: + track_list.append(vector3d(x, y, 0)) + track_list.append(vector3d(x, y, 1)) + + for current in tracks: + if current in track_list: + breakpoint() + + def divide_pin_to_tracks(self, pin, tracks): + """ + Return a list of pin shape parts that are in the tracks. + """ + # If pin is smaller than a track, just return it. + track_pin = self.convert_track_to_shape_pin(list(tracks)[0]) + if pin.width() < track_pin.width() and pin.height() < track_pin.height(): + return [pin] + + overlap_pins = [] + for track in tracks: + track_pin = self.convert_track_to_shape_pin(track) + overlap_pin = track_pin.intersection(pin) + + # If pin is smaller than minwidth, in one dimension, skip it. + min_pin_width = drc("minwidth_{0}". format(pin.layer)) + if not overlap_pin or (overlap_pin.width() < min_pin_width and overlap_pin.height() < min_pin_width): + continue + else: + overlap_pins.append(overlap_pin) + + debug.check(len(overlap_pins) > 0, "No pins overlapped the tracks.") + + return overlap_pins + + def convert_pin_coord_to_tracks(self, pin, coord): """ Return all tracks that an inflated pin overlaps @@ -879,6 +928,8 @@ class router(router_tech): This will mark the grids for all pin components as a source. Marking as source or target also clears blockage status. """ + self.source_name = pin_name + self.source_components = [] for i in range(self.num_pin_components(pin_name)): self.add_pin_component_source(pin_name, i) @@ -890,6 +941,8 @@ class router(router_tech): This will mark the grids for all pin components as a target. Marking as source or target also clears blockage status. """ + self.target_name = pin_name + self.target_components = [] for i in range(self.num_pin_components(pin_name)): self.add_pin_component_target(pin_name, i) @@ -925,42 +978,36 @@ class router(router_tech): self.new_pins[name] = pg.pins - def add_ring_supply_pin(self, name, width=3, space=2): + def add_ring_supply_pin(self, name, width=3, space=3): """ - Adds a ring supply pin that goes inside the given bbox. + Adds a ring supply pin that goes outside the given bbox. """ pg = pin_group(name, [], self) - # Offset two spaces inside and one between the rings - # Units are in routing grids - if name == "gnd": - offset = width + 2 * space - else: - offset = space # LEFT left_grids = set(self.rg.get_perimeter_list(side="left_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[1])) # RIGHT right_grids = set(self.rg.get_perimeter_list(side="right_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[1])) # TOP top_grids = set(self.rg.get_perimeter_list(side="top_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[0])) # BOTTOM bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring", width=width, margin=self.margin, - offset=offset, + offset=space, layers=[0])) horizontal_layer_grids = left_grids | right_grids @@ -972,6 +1019,7 @@ class router(router_tech): # Add vias in the overlap points horizontal_corner_grids = vertical_layer_grids & horizontal_layer_grids + corners = [] for g in horizontal_corner_grids: self.add_via(g) @@ -984,6 +1032,15 @@ class router(router_tech): self.pin_groups[name].append(pg) self.new_pins[name] = pg.pins + # Update the bbox so that it now includes the new pins + for p in pg.pins: + if p.lx() < self.ll.x or p.by() < self.ll.y: + self.ll = p.ll() + if p.rx() > self.ur.x or p.uy() > self.ur.y: + self.ur = p.ur() + self.bbox = (self.ll, self.ur) + self.create_routing_grid() + def get_new_pins(self, name): return self.new_pins[name] @@ -991,6 +1048,8 @@ class router(router_tech): """ This will mark all the cells on the perimeter of the original layout as a target. """ + self.target_name = "" + self.target_components = [] self.rg.add_perimeter_target(side=side) def num_pin_components(self, pin_name): @@ -999,6 +1058,15 @@ class router(router_tech): """ return len(self.pin_groups[pin_name]) + def set_pin_component_source(self, pin_name, index): + """ + Add the pin component but also set it as the exclusive one. + Used by supply routing with multiple components. + """ + self.source_name = pin_name + self.source_components = [] + self.add_pin_component_source(pin_name, index) + def add_pin_component_source(self, pin_name, index): """ This will mark only the pin tracks @@ -1008,6 +1076,7 @@ class router(router_tech): debug.check(index 1: + self.cell.add_route(layers=self.layers, + coordinates=abs_path, + layer_widths=self.layer_widths) else: - # convert the path back to absolute units from tracks - # This assumes 1-track wide again - abs_path = [self.convert_point_to_units(x[0]) for x in path] - # Otherwise, add the route which includes enclosures - if len(self.layers) > 1: - self.cell.add_route(layers=self.layers, - coordinates=abs_path, - layer_widths=self.layer_widths) - else: - self.cell.add_path(layer=self.layers[0], - coordinates=abs_path, - width=self.layer_widths[0]) + self.cell.add_path(layer=self.layers[0], + coordinates=abs_path, + width=self.layer_widths[0]) + + def create_route_connector(self, path_tracks, pin_name, components): + """ + Find a rectangle to connect the track and the off-grid pin of a component. + """ + + if len(path_tracks) == 0 or len(components) == 0: + return + + # Find the track pin + track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks] + + # Convert the off-grid pin into parts in each routing grid + offgrid_pin_parts = [] + for component in components: + pg = self.pin_groups[pin_name][component] + for pin in pg.pins: + # Layer min with + min_width = drc("minwidth_{}".format(pin.layer)) + + # If we intersect, by a min_width, we are done! + for track_pin in track_pins: + intersection = pin.intersection(track_pin) + if intersection and intersection.width() > min_width and intersection.height() > min_width: + return + + #self.break_on_grids(pg.grids, xvals=[68], yvals=range(93,100)) + partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids) + offgrid_pin_parts.extend(partial_pin_parts) + + debug.check(len(offgrid_pin_parts) > 0, "No offgrid pin parts found.") + + # Find closest part + closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts) + + debug.check(closest_track_pin and closest_part_pin, "Found no closest pins.") + + # Find the bbox of the on-grid track and the off-grid pin part + closest_track_pin.bbox([closest_part_pin]) + + # Connect to off grid pin to track pin with closest shape + self.cell.add_rect(layer=closest_track_pin.layer, + offset=closest_track_pin.ll(), + width=closest_track_pin.width(), + height=closest_track_pin.height()) + + def find_closest_pin(self, first_list, second_list): + """ + Find the closest pin in the lists. Does a stupid O(n^2). + """ + min_dist = None + min_item = (None, None) + for pin1 in first_list: + for pin2 in second_list: + if pin1.layer != pin2.layer: + continue + new_dist = pin1.distance(pin2) + if min_dist == None or new_dist < min_dist: + min_item = (pin1, pin2) + min_dist = new_dist + + return min_item + def add_single_enclosure(self, track): """ @@ -1174,7 +1313,15 @@ class router(router_tech): self.paths.append(grid_utils.flatten_set(path)) self.add_route(path) + self.create_route_connector(path, + self.source_name, + self.source_components) + self.create_route_connector(path, + self.target_name, + self.target_components) self.path_blockages.append(self.paths[-1]) + #self.write_debug_gds("debug_route.gds", False) + #breakpoint() return True else: return False @@ -1274,11 +1421,18 @@ class router(router_tech): """ debug.info(2, "Adding router info") + show_bbox = False show_blockages = False show_blockage_grids = False show_enclosures = False show_all_grids = True + if show_bbox: + self.cell.add_rect(layer="text", + offset=vector(self.ll.x, self.ll.y), + width=self.ur.x - self.ll.x, + height=self.ur.y - self.ll.y) + if show_all_grids: for g in self.rg.map: self.annotate_grid(g) diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 6cbbd422..22f1fd15 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -6,8 +6,8 @@ # All rights reserved. # from tech import drc, layer, preferred_directions -from contact import contact -from vector import vector +from base.contact import contact +from base.vector import vector import debug import math diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 7cc41d97..11d689c7 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -5,11 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +from datetime import datetime import debug from globals import print_time -from router import router -from datetime import datetime -from signal_grid import signal_grid +from .router import router +from .signal_grid import signal_grid class signal_escape_router(router): @@ -63,7 +63,7 @@ class signal_escape_router(router): print_time("Maze routing pins",datetime.now(), start_time, 3) - # self.write_debug_gds("final_escape_router.gds",False) + #self.write_debug_gds("final_escape_router.gds",False) return True diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 340db093..f6ea31f5 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -8,10 +8,9 @@ import debug from heapq import heappush,heappop from copy import deepcopy - -from grid import grid -from grid_path import grid_path -from vector3d import vector3d +from base.vector3d import vector3d +from .grid import grid +from .grid_path import grid_path class signal_grid(grid): @@ -119,10 +118,11 @@ class signal_grid(grid): # Expand all directions. neighbors = curpath.expand_dirs() + # Filter the out of region ones # Filter the blocked ones - unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)] + valid_neighbors = [x for x in neighbors if self.is_inside(x) and not self.is_blocked(x)] - return unblocked_neighbors + return valid_neighbors def hpwl(self, src, dest): """ diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index 9fbf871f..11e40a91 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -59,7 +59,7 @@ class signal_router(router): self.write_debug_gds(stop_program=False) return False - self.write_debug_gds(stop_program=False) + #self.write_debug_gds(stop_program=False) return True diff --git a/compiler/router/supply_grid.py b/compiler/router/supply_grid.py index a5f2248f..0ed66c7a 100644 --- a/compiler/router/supply_grid.py +++ b/compiler/router/supply_grid.py @@ -5,8 +5,8 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from signal_grid import signal_grid -from grid_path import grid_path +from .signal_grid import signal_grid +from .grid_path import grid_path class supply_grid(signal_grid): diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 06831299..e592e02d 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -5,14 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +from datetime import datetime import debug from globals import print_time -from vector3d import vector3d -from router import router -from direction import direction -from datetime import datetime -from supply_grid import supply_grid -import grid_utils +from base.vector3d import vector3d +from .router import router +from .direction import direction +from .supply_grid import supply_grid +from . import grid_utils class supply_grid_router(router): diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 88d02f2e..80a386e0 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -5,14 +5,14 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import debug -from globals import print_time -from router import router from datetime import datetime -import grid_utils from scipy.sparse import csr_matrix from scipy.sparse.csgraph import minimum_spanning_tree -from signal_grid import signal_grid +import debug +from globals import print_time +from .router import router +from . import grid_utils +from .signal_grid import signal_grid class supply_tree_router(router): @@ -43,6 +43,7 @@ class supply_tree_router(router): bbox=bbox, route_track_width=self.route_track_width) + def route(self, vdd_name="vdd", gnd_name="gnd"): """ Route the two nets in a single layer. @@ -75,6 +76,9 @@ class supply_tree_router(router): self.add_ring_supply_pin(self.vdd_name) self.add_ring_supply_pin(self.gnd_name) + #self.write_debug_gds("initial_tree_router.gds",False) + #breakpoint() + # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() @@ -82,8 +86,6 @@ class supply_tree_router(router): self.route_pins(gnd_name) print_time("Maze routing supplies", datetime.now(), start_time, 3) - # self.write_debug_gds("final_tree_router.gds",False) - # Did we route everything?? if not self.check_all_routed(vdd_name): return False @@ -144,15 +146,15 @@ class supply_tree_router(router): # Route MST components for index, (src, dest) in enumerate(connections): - if not (index % 100): + if not (index % 25): debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index)) self.route_signal(pin_name, src, dest) - # if pin_name == "gnd": - # print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages)) - # print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) - # self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) + if False and pin_name == "gnd": + print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages)) + print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) + self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) - #self.write_debug_gds("final.gds", True) + #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) #return def route_signal(self, pin_name, src_idx, dest_idx): @@ -161,7 +163,7 @@ class supply_tree_router(router): # Second pass, clear prior pin blockages so that you can route over other metal # of the same supply. Otherwise, this can create a lot of circular routes due to accidental overlaps. for unblock_routes in [False, True]: - for detour_scale in [5 * pow(2, x) for x in range(5)]: + for detour_scale in [2 * pow(2, x) for x in range(5)]: debug.info(2, "Routing {0} to {1} with scale {2}".format(src_idx, dest_idx, detour_scale)) # Clear everything in the routing grid. @@ -169,7 +171,7 @@ class supply_tree_router(router): # This is inefficient since it is non-incremental, but it was # easier to debug. - self.prepare_blockages() + self.prepare_blockages(src=(pin_name, src_idx), dest=(pin_name, dest_idx)) if unblock_routes: msg = "Unblocking supply self blockages to improve access (may cause DRC errors):\n{0}\n{1})" debug.warning(msg.format(pin_name, @@ -178,15 +180,17 @@ class supply_tree_router(router): # Add the single component of the pin as the source # which unmarks it as a blockage too - self.add_pin_component_source(pin_name, src_idx) + self.set_pin_component_source(pin_name, src_idx) # Marks all pin components except index as target # which unmarks it as a blockage too - self.add_pin_component_target(pin_name, dest_idx) + self.set_pin_component_target(pin_name, dest_idx) # Actually run the A* router if self.run_router(detour_scale=detour_scale): return + #if detour_scale > 2: + # self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False) self.write_debug_gds("debug_route.gds", True) diff --git a/compiler/sram/__init__.py b/compiler/sram/__init__.py new file mode 100644 index 00000000..93bb0cbb --- /dev/null +++ b/compiler/sram/__init__.py @@ -0,0 +1,5 @@ +from .sram_1bank import * +from .sram_2bank import * +from .sram_base import * +from .sram_config import * +from .sram import * diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 1fd145b7..0ebb6865 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -6,6 +6,7 @@ # All rights reserved. # from globals import OPTS +import importlib class sram_factory: @@ -101,10 +102,18 @@ class sram_factory: # Load a cached version from previous usage mod = self.modules[real_module_type] except KeyError: - # Dynamically load the module - import importlib - c = importlib.reload(__import__(real_module_type)) + try: + # Dynamically load the module + if real_module_type == "contact": + c = importlib.import_module("base.contact") + else: + c = importlib.import_module("modules."+real_module_type) + except ModuleNotFoundError: + # Check if it is a technology specific module + c = importlib.import_module("custom."+real_module_type) + mod = getattr(c, real_module_type) + self.modules[real_module_type] = mod self.module_indices[real_module_type] = 0 self.objects[real_module_type] = [] diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py index 6851f6a1..7fb5862d 100755 --- a/compiler/tests/00_code_format_check_test.py +++ b/compiler/tests/00_code_format_check_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals import debug diff --git a/compiler/tests/01_library_test.py b/compiler/tests/01_library_test.py index 53151658..35d9eca2 100755 --- a/compiler/tests/01_library_test.py +++ b/compiler/tests/01_library_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py index c39c4c8b..34c96063 100755 --- a/compiler/tests/03_contact_test.py +++ b/compiler/tests/03_contact_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py index c16794de..a08edad7 100755 --- a/compiler/tests/03_path_test.py +++ b/compiler/tests/03_path_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -19,9 +19,9 @@ class path_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import wire_path + from base import wire_path import tech - import design + from base import design min_space = 2 * tech.drc["minwidth_m1"] layer_stack = ("m1") @@ -32,8 +32,8 @@ class path_test(openram_test): [4 * min_space, 3 * min_space ], [0, 3 * min_space ], [0, 6 * min_space ]] - w = design.design("path_test0") - wire_path.wire_path(w,layer_stack, position_list) + w = design("path_test0") + wire_path(w,layer_stack, position_list) self.local_drc_check(w) @@ -49,8 +49,8 @@ class path_test(openram_test): [-1 * min_space, 4 * min_space], [-1 * min_space, 0]] position_list = [[x+min_space, y+min_space] for x,y in old_position_list] - w = design.design("path_test1") - wire_path.wire_path(w,layer_stack, position_list) + w = design("path_test1") + wire_path(w,layer_stack, position_list) self.local_drc_check(w) min_space = 2 * tech.drc["minwidth_m2"] @@ -65,8 +65,8 @@ class path_test(openram_test): [-1 * min_space, 4 * min_space], [-1 * min_space, 0]] position_list = [[x-min_space, y-min_space] for x,y in old_position_list] - w = design.design("path_test2") - wire_path.wire_path(w, layer_stack, position_list) + w = design("path_test2") + wire_path(w, layer_stack, position_list) self.local_drc_check(w) min_space = 2 * tech.drc["minwidth_m3"] @@ -82,8 +82,8 @@ class path_test(openram_test): [-1 * min_space, 0]] # run on the reverse list position_list.reverse() - w = design.design("path_test3") - wire_path.wire_path(w, layer_stack, position_list) + w = design("path_test3") + wire_path(w, layer_stack, position_list) self.local_drc_check(w) globals.end_openram() diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py index 98cc1a5e..2ebf7d45 100755 --- a/compiler/tests/03_ptx_1finger_nmos_test.py +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py index 77bacea2..53922c9b 100755 --- a/compiler/tests/03_ptx_1finger_pmos_test.py +++ b/compiler/tests/03_ptx_1finger_pmos_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py index e5ed6894..a4e18aa2 100755 --- a/compiler/tests/03_ptx_3finger_nmos_test.py +++ b/compiler/tests/03_ptx_3finger_nmos_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py index 29e209e3..b4776456 100755 --- a/compiler/tests/03_ptx_3finger_pmos_test.py +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_ptx_4finger_nmos_test.py b/compiler/tests/03_ptx_4finger_nmos_test.py index 1c8160e4..c1c2184b 100755 --- a/compiler/tests/03_ptx_4finger_nmos_test.py +++ b/compiler/tests/03_ptx_4finger_nmos_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_ptx_4finger_pmos_test.py b/compiler/tests/03_ptx_4finger_pmos_test.py index 408bffbc..fcc843bb 100755 --- a/compiler/tests/03_ptx_4finger_pmos_test.py +++ b/compiler/tests/03_ptx_4finger_pmos_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_ptx_no_contacts_test.py b/compiler/tests/03_ptx_no_contacts_test.py index 745e4f0b..36b53d8f 100755 --- a/compiler/tests/03_ptx_no_contacts_test.py +++ b/compiler/tests/03_ptx_no_contacts_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py index 1de6a7d5..8906e1cf 100755 --- a/compiler/tests/03_wire_test.py +++ b/compiler/tests/03_wire_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys import os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals @@ -19,9 +19,9 @@ class wire_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import wire + from base import wire import tech - import design + from base import design layer_stacks = [tech.poly_stack] + tech.beol_stacks @@ -46,8 +46,8 @@ class wire_test(openram_test): [-1 * min_space, 4 * min_space], [-1 * min_space, 0]] position_list = [[x - min_space, y - min_space] for x, y in position_list] - w = design.design("wire_test_{}".format("_".join(layer_stack))) - wire.wire(w, layer_stack, position_list) + w = design("wire_test_{}".format("_".join(layer_stack))) + wire(w, layer_stack, position_list) self.local_drc_check(w) globals.end_openram() diff --git a/compiler/tests/04_and2_dec_test.py b/compiler/tests/04_and2_dec_test.py index 009a04f1..93ad6d41 100755 --- a/compiler/tests/04_and2_dec_test.py +++ b/compiler/tests/04_and2_dec_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -29,7 +29,16 @@ class and2_dec_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing and2_dec gate") + debug.info(2, "Testing and2_dec 1rw/1r gate") + a = factory.create(module_type="and2_dec") + self.local_check(a) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing and2_dec 1rw gate") a = factory.create(module_type="and2_dec") self.local_check(a) diff --git a/compiler/tests/04_and3_dec_test.py b/compiler/tests/04_and3_dec_test.py index a7cb6ef1..6b6389e3 100755 --- a/compiler/tests/04_and3_dec_test.py +++ b/compiler/tests/04_and3_dec_test.py @@ -9,12 +9,13 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory import debug + class and3_dec_test(openram_test): def runTest(self): @@ -28,7 +29,16 @@ class and3_dec_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing and3_dec gate") + debug.info(2, "Testing and3_dec 1rw/1r gate") + a = factory.create(module_type="and3_dec") + self.local_check(a) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing and3_dec 1rw gate") a = factory.create(module_type="and3_dec") self.local_check(a) diff --git a/compiler/tests/04_and4_dec_test.py b/compiler/tests/04_and4_dec_test.py index ec76435f..c849e6d6 100755 --- a/compiler/tests/04_and4_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -30,7 +30,16 @@ class and4_dec_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing and4_dec gate") + debug.info(2, "Testing and4_dec 1rw/1r gate") + a = factory.create(module_type="and4_dec") + self.local_check(a) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing and4_dec 1rw gate") a = factory.create(module_type="and4_dec") self.local_check(a) diff --git a/compiler/tests/04_column_mux_1rw_1r_test.py b/compiler/tests/04_column_mux_1rw_1r_test.py index 8e45d6c5..56b4301a 100755 --- a/compiler/tests/04_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_column_mux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_column_mux_pbitcell_test.py b/compiler/tests/04_column_mux_pbitcell_test.py index 8ee985d5..4ce02164 100755 --- a/compiler/tests/04_column_mux_pbitcell_test.py +++ b/compiler/tests/04_column_mux_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_column_mux_test.py b/compiler/tests/04_column_mux_test.py index 4254560a..78770b5b 100755 --- a/compiler/tests/04_column_mux_test.py +++ b/compiler/tests/04_column_mux_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_dff_buf_test.py b/compiler/tests/04_dff_buf_test.py index 5064e297..c8d2842e 100755 --- a/compiler/tests/04_dff_buf_test.py +++ b/compiler/tests/04_dff_buf_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_dummy_pbitcell_test.py b/compiler/tests/04_dummy_pbitcell_test.py index ad70820b..0d955283 100755 --- a/compiler/tests/04_dummy_pbitcell_test.py +++ b/compiler/tests/04_dummy_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -20,7 +20,7 @@ class replica_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import dummy_pbitcell + from modules import dummy_pbitcell OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -29,7 +29,7 @@ class replica_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking dummy bitcell using pbitcell (small cell)") - tx = dummy_pbitcell.dummy_pbitcell(name="rpbc") + tx = dummy_pbitcell(name="rpbc") self.local_check(tx) OPTS.num_rw_ports = 1 @@ -38,7 +38,7 @@ class replica_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking dummy bitcell using pbitcell (large cell)") - tx = dummy_pbitcell.dummy_pbitcell(name="rpbc") + tx = dummy_pbitcell(name="rpbc") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/04_pand2_test.py b/compiler/tests/04_pand2_test.py index de08a0e1..d1e9f6ff 100755 --- a/compiler/tests/04_pand2_test.py +++ b/compiler/tests/04_pand2_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,10 +23,10 @@ class pand2_test(openram_test): global verify import verify - import pand2 + from modules import pand2 debug.info(2, "Testing pand2 gate 4x") - a = pand2.pand2(name="pand2x4", size=4) + a = pand2(name="pand2x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py index e27c1050..3b49e3a1 100755 --- a/compiler/tests/04_pand3_test.py +++ b/compiler/tests/04_pand3_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,10 +23,10 @@ class pand3_test(openram_test): global verify import verify - import pand3 + from modules import pand3 debug.info(2, "Testing pand3 gate 4x") - a = pand3.pand3(name="pand3x4", size=4) + a = pand3(name="pand3x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pand4_test.py b/compiler/tests/04_pand4_test.py index cd1a4495..2c5f5878 100755 --- a/compiler/tests/04_pand4_test.py +++ b/compiler/tests/04_pand4_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,10 +23,10 @@ class pand4_test(openram_test): global verify import verify - import pand4 + from modules import pand4 debug.info(2, "Testing pand4 gate 4x") - a = pand4.pand4(name="pand4x4", size=4) + a = pand4(name="pand4x4", size=4) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/04_pbitcell_test.py b/compiler/tests/04_pbitcell_test.py index fa9c05c1..119afc41 100755 --- a/compiler/tests/04_pbitcell_test.py +++ b/compiler/tests/04_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug diff --git a/compiler/tests/04_pbuf_dec_8x_test.py b/compiler/tests/04_pbuf_dec_8x_test.py index c8086653..823f9bcc 100755 --- a/compiler/tests/04_pbuf_dec_8x_test.py +++ b/compiler/tests/04_pbuf_dec_8x_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pbuf_test.py b/compiler/tests/04_pbuf_test.py index 1ee571b3..72a5f2ff 100755 --- a/compiler/tests/04_pbuf_test.py +++ b/compiler/tests/04_pbuf_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pdriver_test.py b/compiler/tests/04_pdriver_test.py index fb95fb31..71ad80cd 100755 --- a/compiler/tests/04_pdriver_test.py +++ b/compiler/tests/04_pdriver_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinv_100x_test.py b/compiler/tests/04_pinv_100x_test.py index c9c23440..0d86e0a8 100755 --- a/compiler/tests/04_pinv_100x_test.py +++ b/compiler/tests/04_pinv_100x_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinv_10x_test.py b/compiler/tests/04_pinv_10x_test.py index 5b8ea70c..8151bf41 100755 --- a/compiler/tests/04_pinv_10x_test.py +++ b/compiler/tests/04_pinv_10x_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinv_1x_beta_test.py b/compiler/tests/04_pinv_1x_beta_test.py index a8cc1b46..9ecbc3aa 100755 --- a/compiler/tests/04_pinv_1x_beta_test.py +++ b/compiler/tests/04_pinv_1x_beta_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinv_1x_test.py b/compiler/tests/04_pinv_1x_test.py index d1a0abf3..f6f4ca04 100755 --- a/compiler/tests/04_pinv_1x_test.py +++ b/compiler/tests/04_pinv_1x_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinv_2x_test.py b/compiler/tests/04_pinv_2x_test.py index aed3bb6e..0ae30973 100755 --- a/compiler/tests/04_pinv_2x_test.py +++ b/compiler/tests/04_pinv_2x_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinv_dec_1x_test.py b/compiler/tests/04_pinv_dec_1x_test.py index 14f046cd..7aee5219 100755 --- a/compiler/tests/04_pinv_dec_1x_test.py +++ b/compiler/tests/04_pinv_dec_1x_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pinvbuf_test.py b/compiler/tests/04_pinvbuf_test.py index 859eed61..4f227181 100755 --- a/compiler/tests/04_pinvbuf_test.py +++ b/compiler/tests/04_pinvbuf_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pnand2_test.py b/compiler/tests/04_pnand2_test.py index 6a8b1f4c..053e343c 100755 --- a/compiler/tests/04_pnand2_test.py +++ b/compiler/tests/04_pnand2_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pnand3_test.py b/compiler/tests/04_pnand3_test.py index a1142b55..a934f698 100755 --- a/compiler/tests/04_pnand3_test.py +++ b/compiler/tests/04_pnand3_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pnand4_test.py b/compiler/tests/04_pnand4_test.py index a47ff7d2..bceff7f9 100755 --- a/compiler/tests/04_pnand4_test.py +++ b/compiler/tests/04_pnand4_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pnor2_test.py b/compiler/tests/04_pnor2_test.py index 117044ce..a68a7190 100755 --- a/compiler/tests/04_pnor2_test.py +++ b/compiler/tests/04_pnor2_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_precharge_1rw_1r_test.py b/compiler/tests/04_precharge_1rw_1r_test.py index a5163e69..026deaee 100755 --- a/compiler/tests/04_precharge_1rw_1r_test.py +++ b/compiler/tests/04_precharge_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_precharge_pbitcell_test.py b/compiler/tests/04_precharge_pbitcell_test.py index 74e2fe54..ae9740fb 100755 --- a/compiler/tests/04_precharge_pbitcell_test.py +++ b/compiler/tests/04_precharge_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 65b9a5f9..3d1d3832 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_pwrite_driver_test.py b/compiler/tests/04_pwrite_driver_test.py index 4f7c6ca3..7d2d525b 100755 --- a/compiler/tests/04_pwrite_driver_test.py +++ b/compiler/tests/04_pwrite_driver_test.py @@ -10,7 +10,7 @@ import unittest from testutils import header, openram_test import sys import os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/04_replica_pbitcell_test.py b/compiler/tests/04_replica_pbitcell_test.py index 8e3ae17e..5b4e7271 100755 --- a/compiler/tests/04_replica_pbitcell_test.py +++ b/compiler/tests/04_replica_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -20,7 +20,7 @@ class replica_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import replica_pbitcell + from modules import replica_pbitcell OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -29,7 +29,7 @@ class replica_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking replica bitcell using pbitcell (small cell)") - tx = replica_pbitcell.replica_pbitcell(name="rpbc") + tx = replica_pbitcell(name="rpbc") self.local_check(tx) OPTS.num_rw_ports = 1 @@ -38,7 +38,7 @@ class replica_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking replica bitcell using pbitcell (large cell)") - tx = replica_pbitcell.replica_pbitcell(name="rpbc") + tx = replica_pbitcell(name="rpbc") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/04_wordline_driver_test.py b/compiler/tests/04_wordline_driver_test.py index 3713ff02..c71e3190 100755 --- a/compiler/tests/04_wordline_driver_test.py +++ b/compiler/tests/04_wordline_driver_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/05_bitcell_array_1rw_1r_test.py b/compiler/tests/05_bitcell_array_1rw_1r_test.py index 6216475d..3ae9a7d7 100755 --- a/compiler/tests/05_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/05_bitcell_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index 916a4356..738a038e 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/05_dummy_array_test.py b/compiler/tests/05_dummy_array_test.py index c6831b90..41eb7b0b 100755 --- a/compiler/tests/05_dummy_array_test.py +++ b/compiler/tests/05_dummy_array_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/05_pbitcell_array_test.py b/compiler/tests/05_pbitcell_array_test.py index ddefb588..8a05dcec 100755 --- a/compiler/tests/05_pbitcell_array_test.py +++ b/compiler/tests/05_pbitcell_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_column_decoder_16row_test.py b/compiler/tests/06_column_decoder_16row_test.py new file mode 100755 index 00000000..d34d6879 --- /dev/null +++ b/compiler/tests/06_column_decoder_16row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class column_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for column_decoder") + a = factory.create(module_type="column_decoder", col_addr_size=4) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py new file mode 100755 index 00000000..26aeb899 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_132row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two + debug.info(1, "Testing 132 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=132) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_132row_test.py b/compiler/tests/06_hierarchical_decoder_132row_test.py new file mode 100755 index 00000000..d1655a25 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_132row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two + debug.info(1, "Testing 132 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=132) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py new file mode 100755 index 00000000..aad79dd4 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_16row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_16row_test.py b/compiler/tests/06_hierarchical_decoder_16row_test.py new file mode 100755 index 00000000..fc379cc4 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_16row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 16 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=16) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py new file mode 100755 index 00000000..d81f6774 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_17row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder with non-power-of-two + debug.info(1, "Testing 17 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_17row_test.py b/compiler/tests/06_hierarchical_decoder_17row_test.py new file mode 100755 index 00000000..ff7d1662 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_17row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder with non-power-of-two + debug.info(1, "Testing 17 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=17) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py deleted file mode 100755 index 867cdaff..00000000 --- a/compiler/tests/06_hierarchical_decoder_1rw_1r_test.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2021 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -import unittest -from testutils import * -import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - - -class hierarchical_decoder_1rw_1r_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - - # Use the 2 port cell since it is usually bigger/easier - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - globals.setup_bitcell() - - # Checks 2x4 and 2-input NAND decoder - debug.info(1, "Testing 16 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=16) - self.local_check(a) - - # Checks 2x4 and 2-input NAND decoder with non-power-of-two - debug.info(1, "Testing 17 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=17) - self.local_check(a) - - # Checks 2x4 with 3x8 and 2-input NAND decoder - debug.info(1, "Testing 32 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=32) - self.local_check(a) - - # Checks 3 x 2x4 and 3-input NAND decoder - debug.info(1, "Testing 64 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=64) - self.local_check(a) - - # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two - debug.info(1, "Testing 132 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=132) - self.local_check(a) - - # Checks 3 x 3x8 and 3-input NAND decoder - debug.info(1, "Testing 512 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=512) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py new file mode 100755 index 00000000..7ec03133 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_32row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 with 3x8 and 2-input NAND decoder + debug.info(1, "Testing 32 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_32row_test.py b/compiler/tests/06_hierarchical_decoder_32row_test.py new file mode 100755 index 00000000..895b63dc --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_32row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 with 3x8 and 2-input NAND decoder + debug.info(1, "Testing 32 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=32) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py new file mode 100755 index 00000000..c0058da4 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_4096row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 4096 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_4096row_test.py b/compiler/tests/06_hierarchical_decoder_4096row_test.py new file mode 100755 index 00000000..14e3f113 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_4096row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 2x4 and 2-input NAND decoder + debug.info(1, "Testing 4096 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py new file mode 100755 index 00000000..5982e930 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_512row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 3 x 3x8 and 3-input NAND decoder + debug.info(1, "Testing 512 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_512row_test.py b/compiler/tests/06_hierarchical_decoder_512row_test.py new file mode 100755 index 00000000..6ad92d86 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_512row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 3 x 3x8 and 3-input NAND decoder + debug.info(1, "Testing 512 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py b/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py new file mode 100755 index 00000000..6dd3b4de --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_64row_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 3 x 2x4 and 3-input NAND decoder + debug.info(1, "Testing 64 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=64) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_64row_test.py b/compiler/tests/06_hierarchical_decoder_64row_test.py new file mode 100755 index 00000000..37b97732 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_64row_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 0 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + # Checks 3 x 2x4 and 3-input NAND decoder + debug.info(1, "Testing 64 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=64) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py index 002317b4..2548e498 100755 --- a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py deleted file mode 100755 index ae55ba3e..00000000 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2021 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -import unittest -from testutils import * -import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - - -class hierarchical_decoder_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 0 - OPTS.num_w_ports = 0 - globals.setup_bitcell() - - # Checks 2x4 and 2-input NAND decoder - debug.info(1, "Testing 16 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=16) - self.local_check(a) - - # Checks 2x4 and 2-input NAND decoder with non-power-of-two - debug.info(1, "Testing 17 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=17) - self.local_check(a) - - # Checks 2x4 with 3x8 and 2-input NAND decoder - debug.info(1, "Testing 32 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=32) - self.local_check(a) - - # Checks 3 x 2x4 and 3-input NAND decoder - debug.info(1, "Testing 64 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=64) - self.local_check(a) - - # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two - debug.info(1, "Testing 132 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=132) - self.local_check(a) - - # Checks 3 x 3x8 and 3-input NAND decoder - debug.info(1, "Testing 512 row sample for hierarchical_decoder") - a = factory.create(module_type="hierarchical_decoder", num_outputs=512) - self.local_check(a) - - # Checks 3 x 4x16 and 4-input NAND decoder - #debug.info(1, "Testing 4096 row sample for hierarchical_decoder") - #a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) - #self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py index 8a33451f..0cff264a 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py index f634225e..bb9523a9 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index ff982cc0..d8680f53 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py index 3c67fb68..d6580991 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py index 9420fd72..1ddc91df 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index eb139537..3ff4a252 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/06_hierarchical_predecode4x16_test.py b/compiler/tests/06_hierarchical_predecode4x16_test.py index 8ebbb58a..ff855601 100755 --- a/compiler/tests/06_hierarchical_predecode4x16_test.py +++ b/compiler/tests/06_hierarchical_predecode4x16_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/19_bank_select_pbitcell_test.py b/compiler/tests/07_column_mux_array_16mux_1rw_1r_test.py similarity index 60% rename from compiler/tests/19_bank_select_pbitcell_test.py rename to compiler/tests/07_column_mux_array_16mux_1rw_1r_test.py index 2f427a35..36d086a8 100755 --- a/compiler/tests/19_bank_select_pbitcell_test.py +++ b/compiler/tests/07_column_mux_array_16mux_1rw_1r_test.py @@ -6,40 +6,37 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory import debug -class bank_select_test(openram_test): + +class column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - OPTS.bitcell = "pbitcell" - debug.info(1, "No column mux, rw control logic") - a = factory.create(module_type="bank_select", port="rw") - self.local_check(a) - - OPTS.num_rw_ports = 0 - OPTS.num_w_ports = 1 + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() - debug.info(1, "No column mux, w control logic") - a = factory.create(module_type="bank_select", port="w") + debug.info(1, "Testing sample for 16-way column_mux_array port 0") + a = factory.create(module_type="column_mux_array", columns=32, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) - debug.info(1, "No column mux, r control logic") - a = factory.create(module_type="bank_select", port="r") + debug.info(1, "Testing sample for 16-way column_mux_array port 1") + a = factory.create(module_type="column_mux_array", columns=32, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) globals.end_openram() + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/19_bank_select_test.py b/compiler/tests/07_column_mux_array_16mux_test.py similarity index 79% rename from compiler/tests/19_bank_select_test.py rename to compiler/tests/07_column_mux_array_16mux_test.py index e3c7ece8..51634eae 100755 --- a/compiler/tests/19_bank_select_test.py +++ b/compiler/tests/07_column_mux_array_16mux_test.py @@ -6,27 +6,28 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory import debug -class bank_select_test(openram_test): + +class column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(1, "No column mux, rw control logic") - a = factory.create(module_type="bank_select", port="rw") + debug.info(1, "Testing sample for 16-way column_mux_array") + a = factory.create(module_type="column_mux_array", columns=64, word_size=4) self.local_check(a) globals.end_openram() + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/tests/07_column_mux_array_test.py b/compiler/tests/07_column_mux_array_2mux_1rw_1r_test.py similarity index 76% rename from compiler/tests/07_column_mux_array_test.py rename to compiler/tests/07_column_mux_array_2mux_1rw_1r_test.py index a3bd8c39..f217720e 100755 --- a/compiler/tests/07_column_mux_array_test.py +++ b/compiler/tests/07_column_mux_array_2mux_1rw_1r_test.py @@ -8,7 +8,7 @@ # from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,16 +21,17 @@ class column_mux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(1, "Testing sample for 2-way column_mux_array") - a = factory.create(module_type="column_mux_array", columns=16, word_size=8) + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(1, "Testing sample for 2-way column_mux_array port 0") + a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) - debug.info(1, "Testing sample for 4-way column_mux_array") - a = factory.create(module_type="column_mux_array", columns=16, word_size=4) - self.local_check(a) - - debug.info(1, "Testing sample for 8-way column_mux_array") - a = factory.create(module_type="column_mux_array", columns=32, word_size=4) + debug.info(1, "Testing sample for 2-way column_mux_array port 1") + a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/07_column_mux_array_2mux_test.py b/compiler/tests/07_column_mux_array_2mux_test.py new file mode 100755 index 00000000..ba5145a4 --- /dev/null +++ b/compiler/tests/07_column_mux_array_2mux_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Testing sample for 2-way column_mux_array") + a = factory.create(module_type="column_mux_array", columns=16, word_size=8) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_column_mux_array_4mux_1rw_1r_test.py b/compiler/tests/07_column_mux_array_4mux_1rw_1r_test.py new file mode 100755 index 00000000..8f4fc364 --- /dev/null +++ b/compiler/tests/07_column_mux_array_4mux_1rw_1r_test.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(1, "Testing sample for 4-way column_mux_array port 0") + a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(a) + + debug.info(1, "Testing sample for 4-way column_mux_array port 1") + a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_column_mux_array_4mux_test.py b/compiler/tests/07_column_mux_array_4mux_test.py new file mode 100755 index 00000000..0d637ef6 --- /dev/null +++ b/compiler/tests/07_column_mux_array_4mux_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Testing sample for 4-way column_mux_array") + a = factory.create(module_type="column_mux_array", columns=16, word_size=4) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_column_mux_array_1rw_1r_test.py b/compiler/tests/07_column_mux_array_8mux_1rw_1r_test.py similarity index 60% rename from compiler/tests/07_column_mux_array_1rw_1r_test.py rename to compiler/tests/07_column_mux_array_8mux_1rw_1r_test.py index d31e4fec..7d2f248e 100755 --- a/compiler/tests/07_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_column_mux_array_8mux_1rw_1r_test.py @@ -8,7 +8,7 @@ # from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -26,22 +26,6 @@ class column_mux_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(1, "Testing sample for 2-way column_mux_array port 0") - a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) - - debug.info(1, "Testing sample for 2-way column_mux_array port 1") - a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(a) - - debug.info(1, "Testing sample for 4-way column_mux_array port 0") - a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(a) - - debug.info(1, "Testing sample for 4-way column_mux_array port 1") - a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(a) - debug.info(1, "Testing sample for 8-way column_mux_array port 0") a = factory.create(module_type="column_mux_array", columns=16, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) diff --git a/compiler/tests/07_column_mux_array_8mux_test.py b/compiler/tests/07_column_mux_array_8mux_test.py new file mode 100755 index 00000000..468d6507 --- /dev/null +++ b/compiler/tests/07_column_mux_array_8mux_test.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class column_mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Testing sample for 8-way column_mux_array") + a = factory.create(module_type="column_mux_array", columns=32, word_size=4) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/07_column_mux_array_pbitcell_test.py b/compiler/tests/07_column_mux_array_pbitcell_test.py index 9e7365c0..bceba919 100755 --- a/compiler/tests/07_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_column_mux_array_pbitcell_test.py @@ -8,7 +8,7 @@ # from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/08_precharge_array_1rw_1r_test.py b/compiler/tests/08_precharge_array_1rw_1r_test.py index 71cd9673..1cb4d12c 100755 --- a/compiler/tests/08_precharge_array_1rw_1r_test.py +++ b/compiler/tests/08_precharge_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index 48bdc17b..39d87476 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/08_wordline_buffer_array_test.py b/compiler/tests/08_wordline_buffer_array_test.py index 3152810d..6ae422c4 100755 --- a/compiler/tests/08_wordline_buffer_array_test.py +++ b/compiler/tests/08_wordline_buffer_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py index b1daff3d..cbfa1825 100755 --- a/compiler/tests/08_wordline_driver_array_1rw_1r_test.py +++ b/compiler/tests/08_wordline_driver_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/08_wordline_driver_array_pbitcell_test.py b/compiler/tests/08_wordline_driver_array_pbitcell_test.py index 0e6cf594..f3398346 100755 --- a/compiler/tests/08_wordline_driver_array_pbitcell_test.py +++ b/compiler/tests/08_wordline_driver_array_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/08_wordline_driver_array_test.py b/compiler/tests/08_wordline_driver_array_test.py index 9436caf0..ea982005 100755 --- a/compiler/tests/08_wordline_driver_array_test.py +++ b/compiler/tests/08_wordline_driver_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/09_sense_amp_array_1rw_1r_test.py b/compiler/tests/09_sense_amp_array_1rw_1r_test.py index 48d3f744..8273a0ad 100755 --- a/compiler/tests/09_sense_amp_array_1rw_1r_test.py +++ b/compiler/tests/09_sense_amp_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/09_sense_amp_array_test_pbitcell.py b/compiler/tests/09_sense_amp_array_pbitcell_test.py similarity index 97% rename from compiler/tests/09_sense_amp_array_test_pbitcell.py rename to compiler/tests/09_sense_amp_array_pbitcell_test.py index d65d8649..4dfd966d 100755 --- a/compiler/tests/09_sense_amp_array_test_pbitcell.py +++ b/compiler/tests/09_sense_amp_array_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys,os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/09_sense_amp_array_spare_cols_test.py b/compiler/tests/09_sense_amp_array_spare_cols_test.py index 8af08d77..57429d2b 100755 --- a/compiler/tests/09_sense_amp_array_spare_cols_test.py +++ b/compiler/tests/09_sense_amp_array_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index d0f38166..aa21f181 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_1rw_1r_test.py b/compiler/tests/10_write_driver_array_1rw_1r_test.py index 233d5c34..534d5904 100755 --- a/compiler/tests/10_write_driver_array_1rw_1r_test.py +++ b/compiler/tests/10_write_driver_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_pbitcell_test.py b/compiler/tests/10_write_driver_array_pbitcell_test.py index e3b2ba2a..cd5601ce 100755 --- a/compiler/tests/10_write_driver_array_pbitcell_test.py +++ b/compiler/tests/10_write_driver_array_pbitcell_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_spare_cols_test.py b/compiler/tests/10_write_driver_array_spare_cols_test.py index 3ee6e485..bacfbdd9 100755 --- a/compiler/tests/10_write_driver_array_spare_cols_test.py +++ b/compiler/tests/10_write_driver_array_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py index 994e210a..fc17301e 100755 --- a/compiler/tests/10_write_driver_array_test.py +++ b/compiler/tests/10_write_driver_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py index b2b29217..02c76300 100755 --- a/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py +++ b/compiler/tests/10_write_driver_array_wmask_pbitcell_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py b/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py index 266fc73a..e609ba43 100755 --- a/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py +++ b/compiler/tests/10_write_driver_array_wmask_spare_cols_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_driver_array_wmask_test.py b/compiler/tests/10_write_driver_array_wmask_test.py index 843d1dc5..7c1fd7bc 100755 --- a/compiler/tests/10_write_driver_array_wmask_test.py +++ b/compiler/tests/10_write_driver_array_wmask_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_mask_and_array_1rw_1r_test.py b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py index c59c4acd..806381b5 100755 --- a/compiler/tests/10_write_mask_and_array_1rw_1r_test.py +++ b/compiler/tests/10_write_mask_and_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_mask_and_array_pbitcell_test.py b/compiler/tests/10_write_mask_and_array_pbitcell_test.py index c389c879..fe43d0f2 100755 --- a/compiler/tests/10_write_mask_and_array_pbitcell_test.py +++ b/compiler/tests/10_write_mask_and_array_pbitcell_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/10_write_mask_and_array_test.py b/compiler/tests/10_write_mask_and_array_test.py index 8cc9b685..8a667145 100755 --- a/compiler/tests/10_write_mask_and_array_test.py +++ b/compiler/tests/10_write_mask_and_array_test.py @@ -10,7 +10,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/11_dff_array_test.py b/compiler/tests/11_dff_array_test.py index d5ee0607..7d4779eb 100755 --- a/compiler/tests/11_dff_array_test.py +++ b/compiler/tests/11_dff_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/11_dff_buf_array_test.py b/compiler/tests/11_dff_buf_array_test.py index 3e1d5eb3..e46a5f91 100755 --- a/compiler/tests/11_dff_buf_array_test.py +++ b/compiler/tests/11_dff_buf_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/12_tri_gate_array_test.py b/compiler/tests/12_tri_gate_array_test.py index 47724f89..354e5188 100755 --- a/compiler/tests/12_tri_gate_array_test.py +++ b/compiler/tests/12_tri_gate_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/13_delay_chain_test.py b/compiler/tests/13_delay_chain_test.py index af1229a3..b07dde74 100755 --- a/compiler/tests/13_delay_chain_test.py +++ b/compiler/tests/13_delay_chain_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py similarity index 53% rename from compiler/tests/14_replica_bitcell_array_1rw_1r_test.py rename to compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py index 0523b471..0f905d75 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_bothrbl_1rw_1r_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -25,21 +25,6 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing 4x4 non-replica array for dp cell") - a = factory.create(module_type="replica_bitcell_array", - cols=4, - rows=4, - rbl=[1, 1]) - self.local_check(a) - - debug.info(2, "Testing 4x4 left replica array for dp cell") - a = factory.create(module_type="replica_bitcell_array", - cols=4, - rows=4, - rbl=[1, 1], - left_rbl=[0]) - self.local_check(a) - debug.info(2, "Testing 4x4 array left and right replica for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, @@ -49,17 +34,6 @@ class replica_bitcell_array_1rw_1r_test(openram_test): right_rbl=[1]) self.local_check(a) - - # Sky 130 has restrictions on the symmetries - if OPTS.tech_name != "sky130": - debug.info(2, "Testing 4x4 array right only replica for dp cell") - a = factory.create(module_type="replica_bitcell_array", - cols=4, - rows=4, - rbl=[1, 1], - right_rbl=[1]) - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py new file mode 100755 index 00000000..a07318f0 --- /dev/null +++ b/compiler/tests/14_replica_bitcell_array_leftrbl_1rw_1r_test.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class replica_bitcell_array_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing 4x4 left replica array for dp cell") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + rbl=[1, 1], + left_rbl=[0]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py new file mode 100755 index 00000000..920a3d3e --- /dev/null +++ b/compiler/tests/14_replica_bitcell_array_norbl_1rw_1r_test.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class replica_bitcell_array_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(2, "Testing 4x4 non-replica array for dp cell") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + rbl=[1, 1]) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/14_replica_bitcell_array_test.py b/compiler/tests/14_replica_bitcell_array_test.py index 33ba6b8e..fc938bfb 100755 --- a/compiler/tests/14_replica_bitcell_array_test.py +++ b/compiler/tests/14_replica_bitcell_array_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/14_replica_column_1rw_1r_test.py b/compiler/tests/14_replica_column_1rw_1r_test.py index 82b6437a..10ee5983 100755 --- a/compiler/tests/14_replica_column_1rw_1r_test.py +++ b/compiler/tests/14_replica_column_1rw_1r_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/14_replica_column_test.py b/compiler/tests/14_replica_column_test.py index c8d50a53..73825e39 100755 --- a/compiler/tests/14_replica_column_test.py +++ b/compiler/tests/14_replica_column_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/14_replica_pbitcell_array_test.py b/compiler/tests/14_replica_pbitcell_array_test.py index b13bc56a..d72289a7 100755 --- a/compiler/tests/14_replica_pbitcell_array_test.py +++ b/compiler/tests/14_replica_pbitcell_array_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/15_global_bitcell_array_1rw_1r_test.py b/compiler/tests/15_global_bitcell_array_1rw_1r_test.py index 9dab6ddd..d7701539 100755 --- a/compiler/tests/15_global_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/15_global_bitcell_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from sram_factory import factory import debug diff --git a/compiler/tests/15_global_bitcell_array_test.py b/compiler/tests/15_global_bitcell_array_test.py index 0f68227e..7f4b8154 100755 --- a/compiler/tests/15_global_bitcell_array_test.py +++ b/compiler/tests/15_global_bitcell_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from sram_factory import factory import debug diff --git a/compiler/tests/15_local_bitcell_array_1rw_1r_test.py b/compiler/tests/15_local_bitcell_array_1rw_1r_test.py index ae86f949..0826586c 100755 --- a/compiler/tests/15_local_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/15_local_bitcell_array_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from sram_factory import factory import debug diff --git a/compiler/tests/15_local_bitcell_array_test.py b/compiler/tests/15_local_bitcell_array_test.py index ae6a4dc0..30c9c8e9 100755 --- a/compiler/tests/15_local_bitcell_array_test.py +++ b/compiler/tests/15_local_bitcell_array_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from sram_factory import factory import debug diff --git a/compiler/tests/16_control_logic_multiport_test.py b/compiler/tests/16_control_logic_multiport_test.py index c90b55de..ae6ce538 100755 --- a/compiler/tests/16_control_logic_multiport_test.py +++ b/compiler/tests/16_control_logic_multiport_test.py @@ -13,7 +13,7 @@ Run a regression test on a control_logic import unittest from testutils import header,openram_test import sys, os -sys.path.append(os.path.join(sys.path[0],"..")) + import globals from globals import OPTS from sram_factory import factory @@ -24,8 +24,6 @@ class control_logic_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import control_logic - import tech # check control logic for multi-port OPTS.bitcell = "pbitcell" diff --git a/compiler/tests/16_control_logic_r_test.py b/compiler/tests/16_control_logic_r_test.py index 74d95189..67fa2635 100755 --- a/compiler/tests/16_control_logic_r_test.py +++ b/compiler/tests/16_control_logic_r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/16_control_logic_rw_test.py b/compiler/tests/16_control_logic_rw_test.py index a6b375e4..dd83f623 100755 --- a/compiler/tests/16_control_logic_rw_test.py +++ b/compiler/tests/16_control_logic_rw_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/16_control_logic_w_test.py b/compiler/tests/16_control_logic_w_test.py index bbdd0c09..4a828fdf 100755 --- a/compiler/tests/16_control_logic_w_test.py +++ b/compiler/tests/16_control_logic_w_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory diff --git a/compiler/tests/18_port_address_16rows_1rw_1r_test.py b/compiler/tests/18_port_address_16rows_1rw_1r_test.py new file mode 100755 index 00000000..ffca57d3 --- /dev/null +++ b/compiler/tests/18_port_address_16rows_1rw_1r_test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_address_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # Use the 2 port cell since it is usually bigger/easier + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_16rows_test.py b/compiler/tests/18_port_address_16rows_test.py new file mode 100755 index 00000000..36093297 --- /dev/null +++ b/compiler/tests/18_port_address_16rows_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_address_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Port address 16 rows") + a = factory.create("port_address", cols=16, rows=16, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_address_1rw_1r_test.py b/compiler/tests/18_port_address_256rows_1rw_1r_test.py similarity index 85% rename from compiler/tests/18_port_address_1rw_1r_test.py rename to compiler/tests/18_port_address_256rows_1rw_1r_test.py index f81196e8..8fef71fc 100755 --- a/compiler/tests/18_port_address_1rw_1r_test.py +++ b/compiler/tests/18_port_address_256rows_1rw_1r_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -26,10 +26,6 @@ class port_address_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(1, "Port address 16 rows") - a = factory.create("port_address", cols=16, rows=16, port=0) - self.local_check(a) - debug.info(1, "Port address 256 rows") a = factory.create("port_address", cols=256, rows=256, port=1) self.local_check(a) diff --git a/compiler/tests/18_port_address_test.py b/compiler/tests/18_port_address_512rows_test.py similarity index 82% rename from compiler/tests/18_port_address_test.py rename to compiler/tests/18_port_address_512rows_test.py index 7ecf3288..03f8c455 100755 --- a/compiler/tests/18_port_address_test.py +++ b/compiler/tests/18_port_address_512rows_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -20,10 +20,6 @@ class port_address_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(1, "Port address 16 rows") - a = factory.create("port_address", cols=16, rows=16, port=0) - self.local_check(a) - debug.info(1, "Port address 512 rows") a = factory.create("port_address", cols=256, rows=512, port=0) self.local_check(a) diff --git a/compiler/tests/18_port_data_16mux_1rw_1r_test.py b/compiler/tests/18_port_data_16mux_1rw_1r_test.py new file mode 100755 index 00000000..15ff8eee --- /dev/null +++ b/compiler/tests/18_port_data_16mux_1rw_1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.word_size=2 + c.num_words=128 + c.words_per_row=16 + factory.reset() + c.recompute_sizes() + debug.info(1, "Sixteen way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_16mux_test.py b/compiler/tests/18_port_data_16mux_test.py new file mode 100755 index 00000000..3415711b --- /dev/null +++ b/compiler/tests/18_port_data_16mux_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.word_size=2 + c.num_words=128 + c.words_per_row=16 + factory.reset() + c.recompute_sizes() + debug.info(1, "Sixteen way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py deleted file mode 100755 index e42ac452..00000000 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2021 Regents of the University of California -# All rights reserved. -# -import unittest -from testutils import * -import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) -import globals -from globals import OPTS -from sram_factory import factory -import debug - - -class port_data_1rw_1r_test(openram_test): - - def runTest(self): - config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) - globals.init_openram(config_file) - from sram_config import sram_config - - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - globals.setup_bitcell() - - c = sram_config(word_size=4, - num_words=16) - - c.words_per_row=1 - factory.reset() - c.recompute_sizes() - debug.info(1, "No column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - c.num_words=32 - c.words_per_row=2 - factory.reset() - c.recompute_sizes() - debug.info(1, "Two way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - c.num_words=64 - c.words_per_row=4 - factory.reset() - c.recompute_sizes() - debug.info(1, "Four way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - factory.reset() - c.recompute_sizes() - debug.info(1, "Eight way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - a = factory.create("port_data", sram_config=c, port=1) - self.local_check(a) - - globals.end_openram() - -# run the test from the command line -if __name__ == "__main__": - (OPTS, args) = globals.parse_args() - del sys.argv[1:] - header(__file__, OPTS.tech_name) - unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_2mux_1rw_1r_test.py b/compiler/tests/18_port_data_2mux_1rw_1r_test.py new file mode 100755 index 00000000..d907b50c --- /dev/null +++ b/compiler/tests/18_port_data_2mux_1rw_1r_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_2mux_test.py b/compiler/tests/18_port_data_2mux_test.py new file mode 100755 index 00000000..4b72d046 --- /dev/null +++ b/compiler/tests/18_port_data_2mux_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_4mux_1rw_1r_test.py b/compiler/tests/18_port_data_4mux_1rw_1r_test.py new file mode 100755 index 00000000..c5d163cf --- /dev/null +++ b/compiler/tests/18_port_data_4mux_1rw_1r_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_4mux_test.py b/compiler/tests/18_port_data_4mux_test.py new file mode 100755 index 00000000..84136361 --- /dev/null +++ b/compiler/tests/18_port_data_4mux_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_test.py b/compiler/tests/18_port_data_8mux_1rw_1r_test.py similarity index 57% rename from compiler/tests/18_port_data_test.py rename to compiler/tests/18_port_data_8mux_1rw_1r_test.py index 3b33466a..17c98ace 100755 --- a/compiler/tests/18_port_data_test.py +++ b/compiler/tests/18_port_data_8mux_1rw_1r_test.py @@ -7,45 +7,28 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory import debug -class port_data_test(openram_test): + +class port_data_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() c = sram_config(word_size=4, num_words=16) - c.words_per_row=1 - factory.reset() - c.recompute_sizes() - debug.info(1, "No column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - - c.num_words=32 - c.words_per_row=2 - factory.reset() - c.recompute_sizes() - debug.info(1, "Two way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - - c.num_words=64 - c.words_per_row=4 - factory.reset() - c.recompute_sizes() - debug.info(1, "Four way column mux") - a = factory.create("port_data", sram_config=c, port=0) - self.local_check(a) - c.word_size=2 c.num_words=128 c.words_per_row=8 @@ -54,6 +37,8 @@ class port_data_test(openram_test): debug.info(1, "Eight way column mux") a = factory.create("port_data", sram_config=c, port=0) self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) globals.end_openram() diff --git a/compiler/tests/18_port_data_8mux_test.py b/compiler/tests/18_port_data_8mux_test.py new file mode 100755 index 00000000..781954cc --- /dev/null +++ b/compiler/tests/18_port_data_8mux_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_nomux_1rw_1r_test.py b/compiler/tests/18_port_data_nomux_1rw_1r_test.py new file mode 100755 index 00000000..1c1197ee --- /dev/null +++ b/compiler/tests/18_port_data_nomux_1rw_1r_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_nomux_test.py b/compiler/tests/18_port_data_nomux_test.py new file mode 100755 index 00000000..7d775cd5 --- /dev/null +++ b/compiler/tests/18_port_data_nomux_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/18_port_data_spare_cols_test.py b/compiler/tests/18_port_data_spare_cols_test.py index 392aef1a..f52e33d4 100755 --- a/compiler/tests/18_port_data_spare_cols_test.py +++ b/compiler/tests/18_port_data_spare_cols_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -19,7 +19,7 @@ class port_data_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=8, num_words=16, diff --git a/compiler/tests/18_port_data_wmask_1rw_1r_test.py b/compiler/tests/18_port_data_wmask_1rw_1r_test.py index 5d72cffc..5f58f670 100755 --- a/compiler/tests/18_port_data_wmask_1rw_1r_test.py +++ b/compiler/tests/18_port_data_wmask_1rw_1r_test.py @@ -7,7 +7,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -19,7 +19,7 @@ class port_data_wmask_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py index 9a4f82d5..406b6822 100755 --- a/compiler/tests/18_port_data_wmask_test.py +++ b/compiler/tests/18_port_data_wmask_test.py @@ -8,7 +8,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -20,11 +20,20 @@ class port_data_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 c = sram_config(word_size=16, write_size=4, - num_words=16) + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row = 1 factory.reset() diff --git a/compiler/tests/19_multi_bank_test.py b/compiler/tests/19_multi_bank_test.py index 46b75697..e73a97fc 100755 --- a/compiler/tests/19_multi_bank_test.py +++ b/compiler/tests/19_multi_bank_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class multi_bank_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_pmulti_bank_test.py b/compiler/tests/19_pmulti_bank_test.py index 099a8214..be2ba0c0 100755 --- a/compiler/tests/19_pmulti_bank_test.py +++ b/compiler/tests/19_pmulti_bank_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class multi_bank_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" # testing layout of bank using pbitcell with 1 RW port (a 6T-cell equivalent) diff --git a/compiler/tests/19_psingle_bank_test.py b/compiler/tests/19_psingle_bank_test.py index 09be0b3f..8aec720c 100755 --- a/compiler/tests/19_psingle_bank_test.py +++ b/compiler/tests/19_psingle_bank_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psingle_bank_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" OPTS.replica_bitcell="replica_pbitcell" diff --git a/compiler/tests/19_single_bank_16mux_1rw_1r_test.py b/compiler/tests/19_single_bank_16mux_1rw_1r_test.py new file mode 100755 index 00000000..adc5d33e --- /dev/null +++ b/compiler/tests/19_single_bank_16mux_1rw_1r_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=2, + num_words=128) + + c.words_per_row=16 + factory.reset() + c.recompute_sizes() + debug.info(1, "Sixteen way column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_16mux_test.py b/compiler/tests/19_single_bank_16mux_test.py new file mode 100755 index 00000000..8dd02db3 --- /dev/null +++ b/compiler/tests/19_single_bank_16mux_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.word_size=2 + c.num_words=128 + c.words_per_row=16 + factory.reset() + c.recompute_sizes() + debug.info(1, "Sixteen way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_1w_1r_test.py b/compiler/tests/19_single_bank_1w_1r_test.py index 6b6fe65c..31b8349b 100755 --- a/compiler/tests/19_single_bank_1w_1r_test.py +++ b/compiler/tests/19_single_bank_1w_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class single_bank_1w_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 0 OPTS.num_r_ports = 1 diff --git a/compiler/tests/19_single_bank_2mux_1rw_1r_test.py b/compiler/tests/19_single_bank_2mux_1rw_1r_test.py new file mode 100755 index 00000000..dbed3cc6 --- /dev/null +++ b/compiler/tests/19_single_bank_2mux_1rw_1r_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_2mux_test.py b/compiler/tests/19_single_bank_2mux_test.py new file mode 100755 index 00000000..f5efc231 --- /dev/null +++ b/compiler/tests/19_single_bank_2mux_test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + c = sram_config(word_size=4, + num_words=16) + + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_4mux_1rw_1r_test.py b/compiler/tests/19_single_bank_4mux_1rw_1r_test.py new file mode 100755 index 00000000..13387ac9 --- /dev/null +++ b/compiler/tests/19_single_bank_4mux_1rw_1r_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_4mux_test.py similarity index 60% rename from compiler/tests/19_single_bank_test.py rename to compiler/tests/19_single_bank_4mux_test.py index c8db9e2f..7a58b401 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_4mux_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,26 +21,11 @@ class single_bank_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=4, num_words=16) - c.words_per_row=1 - factory.reset() - c.recompute_sizes() - debug.info(1, "No column mux") - a = factory.create("bank", sram_config=c) - self.local_check(a) - - c.num_words=32 - c.words_per_row=2 - factory.reset() - c.recompute_sizes() - debug.info(1, "Two way column mux") - a = factory.create("bank", sram_config=c) - self.local_check(a) - c.num_words=64 c.words_per_row=4 factory.reset() @@ -49,15 +34,6 @@ class single_bank_test(openram_test): a = factory.create("bank", sram_config=c) self.local_check(a) - c.word_size=2 - c.num_words=128 - c.words_per_row=8 - factory.reset() - c.recompute_sizes() - debug.info(1, "Eight way column mux") - a = factory.create("bank", sram_config=c) - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/19_single_bank_1rw_1r_test.py b/compiler/tests/19_single_bank_8mux_1rw_1r_test.py similarity index 63% rename from compiler/tests/19_single_bank_1rw_1r_test.py rename to compiler/tests/19_single_bank_8mux_1rw_1r_test.py index 7c94d7a5..f03aadc6 100755 --- a/compiler/tests/19_single_bank_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_8mux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class single_bank_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 @@ -31,29 +31,6 @@ class single_bank_1rw_1r_test(openram_test): c = sram_config(word_size=4, num_words=16) - c.words_per_row=1 - factory.reset() - c.recompute_sizes() - debug.info(1, "No column mux") - a = factory.create(module_type="bank", sram_config=c) - self.local_check(a) - - c.num_words=32 - c.words_per_row=2 - factory.reset() - c.recompute_sizes() - debug.info(1, "Two way column mux") - a = factory.create(module_type="bank", sram_config=c) - self.local_check(a) - - c.num_words=64 - c.words_per_row=4 - factory.reset() - c.recompute_sizes() - debug.info(1, "Four way column mux") - a = factory.create(module_type="bank", sram_config=c) - self.local_check(a) - c.word_size=2 c.num_words=128 c.words_per_row=8 diff --git a/compiler/tests/19_single_bank_8mux_test.py b/compiler/tests/19_single_bank_8mux_test.py new file mode 100755 index 00000000..56a3c549 --- /dev/null +++ b/compiler/tests/19_single_bank_8mux_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.word_size=2 + c.num_words=128 + c.words_per_row=8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_global_bitline.py b/compiler/tests/19_single_bank_global_bitline_test.py similarity index 95% rename from compiler/tests/19_single_bank_global_bitline.py rename to compiler/tests/19_single_bank_global_bitline_test.py index a99534c6..ead8decb 100755 --- a/compiler/tests/19_single_bank_global_bitline.py +++ b/compiler/tests/19_single_bank_global_bitline_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class single_bank_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/19_single_bank_nomux_1rw_1r_test.py b/compiler/tests/19_single_bank_nomux_1rw_1r_test.py new file mode 100755 index 00000000..afb1129b --- /dev/null +++ b/compiler/tests/19_single_bank_nomux_1rw_1r_test.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=4, + num_words=16) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create(module_type="bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_nomux_test.py b/compiler/tests/19_single_bank_nomux_test.py new file mode 100755 index 00000000..6ae084ec --- /dev/null +++ b/compiler/tests/19_single_bank_nomux_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class single_bank_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=4, + num_words=16, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/19_single_bank_spare_cols_test.py b/compiler/tests/19_single_bank_spare_cols_test.py index dd03b18e..97bcf5af 100755 --- a/compiler/tests/19_single_bank_spare_cols_test.py +++ b/compiler/tests/19_single_bank_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -20,7 +20,7 @@ class single_bank_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=4, num_words=16, diff --git a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py index 9fae4bb6..77f7ba2e 100755 --- a/compiler/tests/19_single_bank_wmask_1rw_1r_test.py +++ b/compiler/tests/19_single_bank_wmask_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class single_bank_wmask_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/19_single_bank_wmask_test.py b/compiler/tests/19_single_bank_wmask_test.py index 9e17f105..08fe19f4 100755 --- a/compiler/tests/19_single_bank_wmask_test.py +++ b/compiler/tests/19_single_bank_wmask_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -20,7 +20,7 @@ class single_bank_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=8, diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py index 05927d26..e1970b2f 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psram_1bank_2mux_1rw_1w_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 diff --git a/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py b/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py index 4a17bd05..a666f820 100755 --- a/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1rw_1w_wmask_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psram_1bank_2mux_1rw_1w_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 diff --git a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py index 683b786b..51be2cab 100755 --- a/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_psram_1bank_2mux_1w_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 0 diff --git a/compiler/tests/20_psram_1bank_2mux_test.py b/compiler/tests/20_psram_1bank_2mux_test.py index 758d6b1c..133c81d1 100755 --- a/compiler/tests/20_psram_1bank_2mux_test.py +++ b/compiler/tests/20_psram_1bank_2mux_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psram_1bank_2mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 diff --git a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py index 345758ab..0995e49f 100755 --- a/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_psram_1bank_4mux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psram_1bank_4mux_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 diff --git a/compiler/tests/20_sram_1bank_16mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_16mux_1rw_1r_test.py new file mode 100755 index 00000000..93736a53 --- /dev/null +++ b/compiler/tests/20_sram_1bank_16mux_1rw_1r_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class sram_1bank_8mux_1rw_1r_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + globals.setup_bitcell() + + c = sram_config(word_size=2, + num_words=256, + num_banks=1) + + c.words_per_row=16 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_16mux_test.py b/compiler/tests/20_sram_1bank_16mux_test.py new file mode 100755 index 00000000..6461c5a6 --- /dev/null +++ b/compiler/tests/20_sram_1bank_16mux_test.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class sram_1bank_8mux_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + c = sram_config(word_size=2, + num_words=256, + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) + + c.words_per_row=16 + c.recompute_sizes() + debug.info(1, "Layout test for {}rw,{}r,{}w sram " + "with {} bit words, {} words, {} words per " + "row, {} banks".format(OPTS.num_rw_ports, + OPTS.num_r_ports, + OPTS.num_w_ports, + c.word_size, + c.num_words, + c.words_per_row, + c.num_banks)) + a = factory.create(module_type="sram", sram_config=c) + self.local_check(a, final_verification=True) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py index 82409479..08922ede 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_2mux_1rw_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py index af97d25a..b5d9d776 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_2mux_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py index b19cacc6..abbb2347 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_2mux_1w_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py index 890ad24a..5eabef8b 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class psram_1bank_2mux_1w_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 diff --git a/compiler/tests/20_sram_1bank_2mux_global_test.py b/compiler/tests/20_sram_1bank_2mux_global_test.py index 11f084ec..fc4a32e8 100755 --- a/compiler/tests/20_sram_1bank_2mux_global_test.py +++ b/compiler/tests/20_sram_1bank_2mux_global_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,11 +21,21 @@ class sram_1bank_2mux_global_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.local_array_size = 8 + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, num_words=32, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=2 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_2mux_test.py b/compiler/tests/20_sram_1bank_2mux_test.py index d7240cac..85041348 100755 --- a/compiler/tests/20_sram_1bank_2mux_test.py +++ b/compiler/tests/20_sram_1bank_2mux_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,10 +21,20 @@ class sram_1bank_2mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=32, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=2 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py index 0e0b99e6..d24fa62f 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,12 +21,21 @@ class sram_1bank_2mux_wmask_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, write_size=4, - num_spare_cols=3, num_words=64, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols+2, + num_spare_rows=num_spare_rows) c.words_per_row = 2 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_test.py index a1ef171d..84830d5c 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,11 +21,21 @@ class sram_1bank_2mux_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, write_size=4, num_words=64, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row = 2 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py b/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py index 8631ef57..4df1bcca 100755 --- a/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py +++ b/compiler/tests/20_sram_1bank_32b_1024_wmask_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -22,11 +22,21 @@ class sram_1bank_32b_1024_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=32, write_size=8, num_words=1024, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.recompute_sizes() debug.info(1, "Layout test for {}rw,{}r,{}w sram " diff --git a/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py index 8bcc0415..2e59670f 100755 --- a/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_4mux_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py index 70be4900..0fdf883a 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,10 +21,20 @@ class sram_1bank_4mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=64, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=4 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py index 33ef158f..bc1f824d 100755 --- a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_8mux_test.py b/compiler/tests/20_sram_1bank_8mux_test.py index 6910a1c8..a670744a 100755 --- a/compiler/tests/20_sram_1bank_8mux_test.py +++ b/compiler/tests/20_sram_1bank_8mux_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,10 +21,20 @@ class sram_1bank_8mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=2, num_words=128, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=8 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py index 323678f7..561074a1 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_nomux_1rw_1r_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py index 8b0a1b58..e8e18b7a 100755 --- a/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_nomux_1rw_1r_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,7 +21,7 @@ class sram_1bank_nomux_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py index bd9645e3..99131037 100755 --- a/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_spare_cols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,11 +21,20 @@ class sram_1bank_nomux_spare_cols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, - num_spare_cols=3, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols+2, + num_spare_rows=num_spare_rows) c.words_per_row = 1 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_nomux_test.py b/compiler/tests/20_sram_1bank_nomux_test.py index 098b8b12..59e309df 100755 --- a/compiler/tests/20_sram_1bank_nomux_test.py +++ b/compiler/tests/20_sram_1bank_nomux_test.py @@ -9,7 +9,6 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory @@ -21,10 +20,20 @@ class sram_1bank_nomux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py index ca6361b8..a7326df3 100755 --- a/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py +++ b/compiler/tests/20_sram_1bank_nomux_wmask_sparecols_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -22,12 +22,21 @@ class sram_1bank_nomux_wmask_sparecols_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, write_size=4, num_words=16, - num_spare_cols=3, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols+2, + num_spare_rows=num_spare_rows) c.words_per_row = 1 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_nomux_wmask_test.py b/compiler/tests/20_sram_1bank_nomux_wmask_test.py index 83dea577..5eb10212 100755 --- a/compiler/tests/20_sram_1bank_nomux_wmask_test.py +++ b/compiler/tests/20_sram_1bank_nomux_wmask_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -21,11 +21,21 @@ class sram_1bank_nomux_wmask_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, write_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols+2, + num_spare_rows=num_spare_rows) c.words_per_row = 1 c.recompute_sizes() diff --git a/compiler/tests/20_sram_1bank_ring_test.py b/compiler/tests/20_sram_1bank_ring_test.py index e34920c2..5010a3de 100755 --- a/compiler/tests/20_sram_1bank_ring_test.py +++ b/compiler/tests/20_sram_1bank_ring_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -22,10 +22,20 @@ class sram_1bank_nomux_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) OPTS.supply_pin_type = "ring" - from sram_config import sram_config + from modules import sram_config + + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py index da672801..e2d144bf 100755 --- a/compiler/tests/20_sram_2bank_test.py +++ b/compiler/tests/20_sram_2bank_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -22,7 +22,7 @@ class sram_2bank_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=16, num_words=32, num_banks=2) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index 584e705f..a5677a13 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -9,12 +9,13 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory import debug +@unittest.skip("SKIPPING 21_hspice_delay_test") class timing_sram_test(openram_test): def runTest(self): @@ -29,10 +30,19 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 9154502e..0ebcb167 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -9,11 +9,12 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS +@unittest.skip("SKIPPING 21_hspice_setuphold_test") class timing_setup_test(openram_test): def runTest(self): diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index cf320208..ab6f3888 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,11 +31,20 @@ class model_delay_test(openram_test): reload(characterizer) from characterizer import delay from characterizer import elmore - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=1, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py index f5bcc658..3935ac3d 100755 --- a/compiler/tests/21_ngspice_delay_extra_rows_test.py +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -29,7 +29,7 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=1, num_words=16, num_banks=1, diff --git a/compiler/tests/21_ngspice_delay_global_test.py b/compiler/tests/21_ngspice_delay_global_test.py index 78b764f4..94ed2aba 100755 --- a/compiler/tests/21_ngspice_delay_global_test.py +++ b/compiler/tests/21_ngspice_delay_global_test.py @@ -9,13 +9,14 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory import debug +@unittest.skip("SKIPPING 21_ngspice_delay_global_test") class timing_sram_test(openram_test): def runTest(self): @@ -30,11 +31,20 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram_config import sram_config + from modules import sram_config OPTS.local_array_size = 2 + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() # c = sram_config(word_size=8, diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 15fdaca3..d5b952ac 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -29,10 +29,19 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") diff --git a/compiler/tests/21_ngspice_setuphold_test.py b/compiler/tests/21_ngspice_setuphold_test.py index 9bda2c2c..70adfaaa 100755 --- a/compiler/tests/21_ngspice_setuphold_test.py +++ b/compiler/tests/21_ngspice_setuphold_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS diff --git a/compiler/tests/21_regression_delay_test.py b/compiler/tests/21_regression_delay_test.py index 9d0c50c8..dbebba0d 100755 --- a/compiler/tests/21_regression_delay_test.py +++ b/compiler/tests/21_regression_delay_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,11 +31,20 @@ class regression_model_test(openram_test): reload(characterizer) from characterizer import linear_regression from characterizer import neural_network - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=1, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") diff --git a/compiler/tests/21_xyce_delay_test.py b/compiler/tests/21_xyce_delay_test.py index 16fdc2f6..705f432e 100755 --- a/compiler/tests/21_xyce_delay_test.py +++ b/compiler/tests/21_xyce_delay_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -30,10 +30,19 @@ class timing_sram_test(openram_test): import characterizer reload(characterizer) from characterizer import delay - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") diff --git a/compiler/tests/21_xyce_setuphold_test.py b/compiler/tests/21_xyce_setuphold_test.py index 3aabce06..c2962c48 100755 --- a/compiler/tests/21_xyce_setuphold_test.py +++ b/compiler/tests/21_xyce_setuphold_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS diff --git a/compiler/tests/22_psram_1bank_2mux_func_test.py b/compiler/tests/22_psram_1bank_2mux_func_test.py index f5f4cd8f..9d6d038e 100755 --- a/compiler/tests/22_psram_1bank_2mux_func_test.py +++ b/compiler/tests/22_psram_1bank_2mux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -38,7 +38,7 @@ class psram_1bank_2mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=2, num_words=32, num_banks=1) diff --git a/compiler/tests/22_psram_1bank_4mux_func_test.py b/compiler/tests/22_psram_1bank_4mux_func_test.py index f422728b..30943de1 100755 --- a/compiler/tests/22_psram_1bank_4mux_func_test.py +++ b/compiler/tests/22_psram_1bank_4mux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -39,7 +39,7 @@ class psram_1bank_4mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=2, num_words=256, num_banks=1) diff --git a/compiler/tests/22_psram_1bank_8mux_func_test.py b/compiler/tests/22_psram_1bank_8mux_func_test.py index f0247808..dd79e6b8 100755 --- a/compiler/tests/22_psram_1bank_8mux_func_test.py +++ b/compiler/tests/22_psram_1bank_8mux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -39,7 +39,7 @@ class psram_1bank_8mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=4, num_words=256, num_banks=1) diff --git a/compiler/tests/22_psram_1bank_nomux_func_test.py b/compiler/tests/22_psram_1bank_nomux_func_test.py index 203fd0ac..e81cc87d 100755 --- a/compiler/tests/22_psram_1bank_nomux_func_test.py +++ b/compiler/tests/22_psram_1bank_nomux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -38,7 +38,7 @@ class psram_1bank_nomux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=2, num_words=32, num_banks=1) diff --git a/compiler/tests/22_sram_1bank_2mux_func_test.py b/compiler/tests/22_sram_1bank_2mux_func_test.py index cca6aeeb..c7ac0d05 100755 --- a/compiler/tests/22_sram_1bank_2mux_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,10 +31,19 @@ class sram_1bank_2mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=32, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=2 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_2mux_global_func_test.py b/compiler/tests/22_sram_1bank_2mux_global_func_test.py index 14de7ba8..dc4b1730 100755 --- a/compiler/tests/22_sram_1bank_2mux_global_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_global_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,11 +31,20 @@ class sram_1bank_2mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config OPTS.local_array_size = 8 + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, num_words=32, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=2 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py index 0e3a0f27..20570a6c 100755 --- a/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py +++ b/compiler/tests/22_sram_1bank_2mux_sparecols_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,11 +31,19 @@ class sram_1bank_2mux_sparecols_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=32, - num_spare_cols=3, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols+2, + num_spare_rows=num_spare_rows) c.words_per_row=2 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_4mux_func_test.py b/compiler/tests/22_sram_1bank_4mux_func_test.py index 4f7035cc..7b397209 100755 --- a/compiler/tests/22_sram_1bank_4mux_func_test.py +++ b/compiler/tests/22_sram_1bank_4mux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,10 +31,19 @@ class sram_1bank_4mux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=128, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=4 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_8mux_func_test.py b/compiler/tests/22_sram_1bank_8mux_func_test.py index cc6456cf..fcc38091 100755 --- a/compiler/tests/22_sram_1bank_8mux_func_test.py +++ b/compiler/tests/22_sram_1bank_8mux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -34,10 +34,19 @@ class sram_1bank_8mux_func_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=128, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=8 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py b/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py index c0518957..76116485 100755 --- a/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_1rw_1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -35,7 +35,7 @@ class psram_1bank_nomux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=4, num_words=32, num_banks=1) diff --git a/compiler/tests/22_sram_1bank_nomux_func_test.py b/compiler/tests/22_sram_1bank_nomux_func_test.py index ff33ead5..5617b3fe 100755 --- a/compiler/tests/22_sram_1bank_nomux_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,10 +31,19 @@ class sram_1bank_nomux_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py index a2fa6d88..5b3e0908 100755 --- a/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py +++ b/compiler/tests/22_sram_1bank_nomux_sparecols_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,11 +31,19 @@ class sram_1bank_nomux_sparecols_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=4, num_words=16, - num_spare_cols=3, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols+2, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py b/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py index 6808462e..09b77170 100755 --- a/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py +++ b/compiler/tests/22_sram_1bank_wmask_1rw_1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -35,11 +35,20 @@ class sram_wmask_1w_1r_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, num_words=16, write_size=2, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row = 1 c.recompute_sizes() debug.info(1, diff --git a/compiler/tests/22_sram_wmask_func_test.py b/compiler/tests/22_sram_wmask_func_test.py index f41b3d98..570b515e 100755 --- a/compiler/tests/22_sram_wmask_func_test.py +++ b/compiler/tests/22_sram_wmask_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -31,11 +31,20 @@ class sram_wmask_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + c = sram_config(word_size=8, num_words=16, write_size=4, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Functional test for sram with " diff --git a/compiler/tests/23_lib_sram_linear_regression_test.py b/compiler/tests/23_lib_sram_linear_regression_test.py index bdca8fdd..642c3b51 100755 --- a/compiler/tests/23_lib_sram_linear_regression_test.py +++ b/compiler/tests/23_lib_sram_linear_regression_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -24,12 +24,21 @@ class lib_sram_linear_regression_test(openram_test): OPTS.netlist_only = True OPTS.model_name = "linear_regression" + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + from characterizer import lib - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") diff --git a/compiler/tests/23_lib_sram_model_corners_test.py b/compiler/tests/23_lib_sram_model_corners_test.py index 3f989b61..7a46aec5 100755 --- a/compiler/tests/23_lib_sram_model_corners_test.py +++ b/compiler/tests/23_lib_sram_model_corners_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,12 +23,21 @@ class lib_model_corners_lib_test(openram_test): OPTS.nominal_corner_only = False OPTS.netlist_only = True + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + from characterizer import lib - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") diff --git a/compiler/tests/23_lib_sram_model_test.py b/compiler/tests/23_lib_sram_model_test.py index e729a9bd..ec918b00 100755 --- a/compiler/tests/23_lib_sram_model_test.py +++ b/compiler/tests/23_lib_sram_model_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,12 +23,21 @@ class lib_sram_model_test(openram_test): OPTS.nominal_corner_only = False OPTS.netlist_only = True + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + from characterizer import lib - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank") diff --git a/compiler/tests/23_lib_sram_prune_test.py b/compiler/tests/23_lib_sram_prune_test.py index b4efd1dd..b509a1b9 100755 --- a/compiler/tests/23_lib_sram_prune_test.py +++ b/compiler/tests/23_lib_sram_prune_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -32,11 +32,20 @@ class lib_sram_prune_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram - from sram_config import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank") diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py index a894b328..34397ada 100755 --- a/compiler/tests/23_lib_sram_test.py +++ b/compiler/tests/23_lib_sram_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os,re -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -30,11 +30,20 @@ class lib_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - from sram import sram - from sram_config import sram_config + if OPTS.tech_name == "sky130": + num_spare_rows = 1 + num_spare_cols = 1 + else: + num_spare_rows = 0 + num_spare_cols = 0 + + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, - num_banks=1) + num_banks=1, + num_spare_cols=num_spare_cols, + num_spare_rows=num_spare_rows) c.words_per_row=1 c.recompute_sizes() debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py index 43638003..675daed8 100755 --- a/compiler/tests/24_lef_sram_test.py +++ b/compiler/tests/24_lef_sram_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,8 +23,8 @@ class lef_test(openram_test): globals.init_openram(config_file) OPTS.route_supplies=False OPTS.check_lvsdrc=False - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, num_banks=1) diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index 63658d04..be2528b6 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug @@ -23,8 +23,8 @@ class verilog_test(openram_test): OPTS.route_supplies=False OPTS.check_lvsdrc=False OPTS.netlist_only=True - from sram import sram - from sram_config import sram_config + from modules import sram + from modules import sram_config c = sram_config(word_size=2, num_words=16, num_banks=1) diff --git a/compiler/tests/26_hspice_pex_pinv_test.py b/compiler/tests/26_hspice_pex_pinv_test.py index 3246913b..7dbd56a6 100755 --- a/compiler/tests/26_hspice_pex_pinv_test.py +++ b/compiler/tests/26_hspice_pex_pinv_test.py @@ -11,7 +11,7 @@ with HSPICE. import unittest from testutils import header, openram_test import sys, os -sys.path.append(os.path.join(sys.path[0],"..")) + import globals from globals import OPTS import debug diff --git a/compiler/tests/26_ngspice_pex_pinv_test.py b/compiler/tests/26_ngspice_pex_pinv_test.py index d53f2d2c..c2582237 100755 --- a/compiler/tests/26_ngspice_pex_pinv_test.py +++ b/compiler/tests/26_ngspice_pex_pinv_test.py @@ -11,7 +11,7 @@ with Ngspice. import unittest from testutils import header,openram_test import sys, os -sys.path.append(os.path.join(sys.path[0],"..")) + import globals from globals import OPTS import debug diff --git a/compiler/tests/26_sram_pex_test.py b/compiler/tests/26_sram_pex_test.py index 90dd46ee..c8b101cb 100755 --- a/compiler/tests/26_sram_pex_test.py +++ b/compiler/tests/26_sram_pex_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -30,7 +30,7 @@ class sram_pex_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=4, num_words=32, num_banks=1) diff --git a/compiler/tests/30_openram_back_end_test.py b/compiler/tests/30_openram_back_end_test.py index c67b8249..e0233ead 100755 --- a/compiler/tests/30_openram_back_end_test.py +++ b/compiler/tests/30_openram_back_end_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os, re, shutil -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug diff --git a/compiler/tests/30_openram_front_end_test.py b/compiler/tests/30_openram_front_end_test.py index 87b280dc..489ea26c 100755 --- a/compiler/tests/30_openram_front_end_test.py +++ b/compiler/tests/30_openram_front_end_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os, re, shutil -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS import debug diff --git a/compiler/tests/50_riscv_1k_1rw1r_func_test.py b/compiler/tests/50_riscv_1k_1rw1r_func_test.py index 6cd494aa..2b770111 100755 --- a/compiler/tests/50_riscv_1k_1rw1r_func_test.py +++ b/compiler/tests/50_riscv_1k_1rw1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=256, diff --git a/compiler/tests/50_riscv_1k_1rw_func_test.py b/compiler/tests/50_riscv_1k_1rw_func_test.py index a99fba03..9623f4af 100755 --- a/compiler/tests/50_riscv_1k_1rw_func_test.py +++ b/compiler/tests/50_riscv_1k_1rw_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=256, diff --git a/compiler/tests/50_riscv_1rw1r_func_test.py b/compiler/tests/50_riscv_1rw1r_func_test.py index 19609159..4b864193 100755 --- a/compiler/tests/50_riscv_1rw1r_func_test.py +++ b/compiler/tests/50_riscv_1rw1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -36,7 +36,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=32, diff --git a/compiler/tests/50_riscv_1rw1r_phys_test.py b/compiler/tests/50_riscv_1rw1r_phys_test.py index 40ae942f..47774828 100755 --- a/compiler/tests/50_riscv_1rw1r_phys_test.py +++ b/compiler/tests/50_riscv_1rw1r_phys_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -22,7 +22,7 @@ class riscv_phys_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/50_riscv_1rw_func_test.py b/compiler/tests/50_riscv_1rw_func_test.py index 0e7e79d7..ee157a44 100755 --- a/compiler/tests/50_riscv_1rw_func_test.py +++ b/compiler/tests/50_riscv_1rw_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -44,7 +44,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=64, diff --git a/compiler/tests/50_riscv_1rw_phys_test.py b/compiler/tests/50_riscv_1rw_phys_test.py index 4cf2def5..c4e574c1 100755 --- a/compiler/tests/50_riscv_1rw_phys_test.py +++ b/compiler/tests/50_riscv_1rw_phys_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -22,7 +22,7 @@ class riscv_phys_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - from sram_config import sram_config + from modules import sram_config if OPTS.tech_name == "sky130": num_spare_rows = 1 diff --git a/compiler/tests/50_riscv_2k_1rw1r_func_test.py b/compiler/tests/50_riscv_2k_1rw1r_func_test.py index 2fa1ec47..ffa7ca03 100755 --- a/compiler/tests/50_riscv_2k_1rw1r_func_test.py +++ b/compiler/tests/50_riscv_2k_1rw1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=512, diff --git a/compiler/tests/50_riscv_2k_1rw_func_test.py b/compiler/tests/50_riscv_2k_1rw_func_test.py index 24ec2e3a..a48a03c0 100755 --- a/compiler/tests/50_riscv_2k_1rw_func_test.py +++ b/compiler/tests/50_riscv_2k_1rw_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=512, diff --git a/compiler/tests/50_riscv_4k_1rw1r_func_test.py b/compiler/tests/50_riscv_4k_1rw1r_func_test.py index 6b4f336e..431b5390 100755 --- a/compiler/tests/50_riscv_4k_1rw1r_func_test.py +++ b/compiler/tests/50_riscv_4k_1rw1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=1024, diff --git a/compiler/tests/50_riscv_4k_1rw_func_test.py b/compiler/tests/50_riscv_4k_1rw_func_test.py index 9f2dc2f6..45254ac4 100755 --- a/compiler/tests/50_riscv_4k_1rw_func_test.py +++ b/compiler/tests/50_riscv_4k_1rw_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=1024, diff --git a/compiler/tests/50_riscv_512b_1rw1r_func_test.py b/compiler/tests/50_riscv_512b_1rw1r_func_test.py index 17b3f518..26682139 100755 --- a/compiler/tests/50_riscv_512b_1rw1r_func_test.py +++ b/compiler/tests/50_riscv_512b_1rw1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=128, diff --git a/compiler/tests/50_riscv_512b_1rw_func_test.py b/compiler/tests/50_riscv_512b_1rw_func_test.py index 4cceefd8..0af228a9 100755 --- a/compiler/tests/50_riscv_512b_1rw_func_test.py +++ b/compiler/tests/50_riscv_512b_1rw_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=128, diff --git a/compiler/tests/50_riscv_8k_1rw1r_func_test.py b/compiler/tests/50_riscv_8k_1rw1r_func_test.py index 0783823c..f2861bad 100755 --- a/compiler/tests/50_riscv_8k_1rw1r_func_test.py +++ b/compiler/tests/50_riscv_8k_1rw1r_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=2048, diff --git a/compiler/tests/50_riscv_8k_1rw_func_test.py b/compiler/tests/50_riscv_8k_1rw_func_test.py index 7b948aad..22db186e 100755 --- a/compiler/tests/50_riscv_8k_1rw_func_test.py +++ b/compiler/tests/50_riscv_8k_1rw_func_test.py @@ -9,7 +9,7 @@ import unittest from testutils import * import sys, os -sys.path.append(os.getenv("OPENRAM_HOME")) + import globals from globals import OPTS from sram_factory import factory @@ -37,7 +37,7 @@ class riscv_func_test(openram_test): import characterizer reload(characterizer) from characterizer import functional - from sram_config import sram_config + from modules import sram_config c = sram_config(word_size=32, write_size=8, num_words=2048, diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile new file mode 100644 index 00000000..f7bbc91b --- /dev/null +++ b/compiler/tests/Makefile @@ -0,0 +1,133 @@ +TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))../..) +include $(TOP_DIR)/openram.mk + +.DEFAULT_GOAL := all + +ARGS ?= +TEST_TECHS ?= scn4m_subm freepdk45 +TECHS ?= scn4m_subm freepdk45 sky130 + +TEST_DIR = $(TOP_DIR)/compiler/tests +TEST_SRCS = $(sort $(notdir $(wildcard $(TEST_DIR)/*_test.py))) +TEST_BASES = $(basename $(TEST_SRCS)) +TEST_STAMPS= $(addsuffix .ok,$(TEST_BASES)) + +OPENRAM_DIR = /openram/compiler/tests +RESULTS_DIR = $(OPENRAM_DIR)/results + +# Use % for all techs: +# %/test.py +# or a specific tech: +# freepdk45/test.py +BROKEN_STAMPS = \ + sky130/01_library_test.ok \ + sky130/04_column_mux_pbitcell_test.ok \ + sky130/04_dummy_pbitcell_test.ok \ + sky130/04_pbitcell_test.ok \ + sky130/04_pnand4_test.ok \ + sky130/04_pand4_test.ok \ + sky130/04_precharge_pbitcell_test.ok \ + sky130/04_replica_pbitcell_test.ok \ + sky130/05_pbitcell_array_test.ok \ + sky130/05_bitcell_array_test.ok \ + sky130/05_bitcell_array_1rw_1r_test.ok \ + sky130/05_dummy_array_test.ok \ + %/06_hierarchical_decoder_4096row_test.ok \ + sky130/07_column_mux_array_pbitcell_test.ok \ + sky130/19_pmulti_bank_test.ok \ + sky130/19_psingle_bank_test.ok \ + sky130/19_bank_select_pbitcell_test.ok \ + %/19_single_bank_16mux_1rw_1r_test.ok \ + %/19_single_bank_16mux_test.ok \ + %/20_sram_1bank_16mux_1rw_1r_test.ok \ + %/20_sram_1bank_16mux_test.ok \ + %/20_psram_1bank_2mux_1rw_1w_test.ok \ + %/20_psram_1bank_2mux_1rw_1w_wmask_test.ok \ + %/20_psram_1bank_2mux_1w_1r_test.ok \ + %/20_psram_1bank_2mux_test.ok \ + %/21_hspice_delay_test.ok \ + %/21_hspice_setuphold_test.ok \ + sky130/20_psram_1bank_4mux_1rw_1r_test.ok \ + sky130/22_psram_1bank_2mux_func_test.ok \ + sky130/22_psram_1bank_4mux_func_test.ok \ + sky130/22_psram_1bank_8mux_func_test.ok \ + sky130/22_psram_1bank_nomux_func_test.ok \ + %/22_psram_1bank_2mux_func_test.ok \ + %/22_psram_1bank_4mux_func_test.ok \ + %/22_psram_1bank_8mux_func_test.ok \ + %/22_psram_1bank_nomux_func_test.ok \ + %/22_sram_1bank_2mux_func_test.ok \ + %/22_sram_1bank_2mux_global_func_test.ok \ + %/22_sram_1bank_2mux_sparecols_func_test.ok \ + %/22_sram_1bank_4mux_func_test.ok \ + %/22_sram_1bank_8mux_func_test.ok \ + %/22_sram_1bank_nomux_1rw_1r_func_test.ok \ + %/22_sram_1bank_nomux_func_test.ok \ + %/22_sram_1bank_nomux_sparecols_func_test.ok \ + %/22_sram_1bank_wmask_1rw_1r_func_test.ok \ + %/22_sram_wmask_func_test.ok \ + sky130/23_lib_sram_linear_regression_test.ok \ + sky130/23_lib_sram_model_corners_test.ok \ + sky130/23_lib_sram_model_test.ok \ + sky130/23_lib_sram_prune_test.ok \ + sky131/23_lib_sram_test.ok \ + %/26_hspice_pex_pinv_test.ok \ + %/50_riscv_1k_1rw1r_func_test.ok \ + %/50_riscv_1k_1rw_func_test.ok \ + %/50_riscv_1rw1r_func_test.ok \ + %/50_riscv_1rw1r_phys_test.ok \ + %/50_riscv_1rw_func_test.ok \ + %/50_riscv_1rw_phys_test.ok \ + %/50_riscv_2k_1rw1r_func_test.ok \ + %/50_riscv_2k_1rw_func_test.ok \ + %/50_riscv_4k_1rw1r_func_test.ok \ + %/50_riscv_4k_1rw_func_test.ok \ + %/50_riscv_512b_1rw1r_func_test.ok \ + %/50_riscv_512b_1rw_func_test.ok \ + %/50_riscv_8k_1rw1r_func_test.ok \ + %/50_riscv_8k_1rw_func_test.ok + +gettech = $(word 1,$(subst /, ,$*)) +getfile = $(word 2,$(subst /, ,$*)) +TECH_TEST_STAMPS=$(foreach T, $(TEST_TECHS), $(addprefix $T/, $(TEST_STAMPS))) + +# Filter out the tests after creating the tech stamps +WORKING_TECH_TEST_STAMPS=$(shell shuf -e -- $(filter-out $(BROKEN_STAMPS), $(TECH_TEST_STAMPS))) + + +# Run all technologies +all: clean $(WORKING_TECH_TEST_STAMPS) + @ls -1 $(TOP_DIR)/compiler/tests/results/*/*.bad 1> /dev/null 2>&1 && echo "FAILING TESTS" && ls -1 $(TOP_DIR)/compiler/tests/results/*/*.bad | sed -e "s#^.*results\/##" && exit 1 || exit 0 +.PHONY: all + +# Run a given technology +# e.g. make freepdk45 +$(TECHS): + @$(MAKE) --no-print-directory $(filter-out $(BROKEN_STAMPS), $(addprefix $@/, $(TEST_STAMPS))) +.PHONY: $(TECHS) + +# Targets for each individual test in all technologies +# e.g. make 04_pinv_1x_test +$(TEST_BASES): + @$(MAKE) --no-print-directory $(foreach T, $(TECHS), $(addprefix $T/, $@.ok)) +.PHONY: $(TEST_BASES) + +# To run a test in a given technology +%.ok: +# @echo "Running $(gettech) $(getfile) ... " + @rm -rf results/$* + @rm -rf results/$*.* + @mkdir -p results/$*/tmp + @$(DOCKER_CMD) sh -c ". /home/cad-user/.bashrc && sleep 1 && python3 -u $(OPENRAM_DIR)/$(getfile).py \ + -t $(gettech) -k -v $(ARGS) -p $(OPENRAM_DIR)/results/$* > $(OPENRAM_DIR)/results/$*.out 2>&1 && touch $(OPENRAM_DIR)/results/$*.ok || touch $(OPENRAM_DIR)/results/$*.bad" + @test -f $(TOP_DIR)/compiler/tests/results/$*.ok && echo "$* ... PASS!" && \ + rm -rf $(TOP_DIR)/compiler/tests/results/$* || echo "$* ... FAIL!" +.DELETE_ON_ERROR: $(TEST_STAMPS) + +.PHONY: docker-pull +docker-pull: + docker pull vlsida/openram-ubuntu:latest + +clean: + @rm -rf $(TOP_DIR)/compiler/tests/results +.PHONE: clean diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index dc3739ab..44c3e774 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -14,3 +14,6 @@ tech_name = OPTS.tech_name nominal_corner_only = True check_lvsdrc = True +#route_supplies = False + +output_name = "sram" diff --git a/compiler/tests/configs/config_back_end.py b/compiler/tests/configs/config_back_end.py index 3294e979..4bf0aa8b 100644 --- a/compiler/tests/configs/config_back_end.py +++ b/compiler/tests/configs/config_back_end.py @@ -15,4 +15,5 @@ nominal_corner_only = True check_lvsdrc = True spice_name = "ngspice" +output_name = "sram" diff --git a/compiler/tests/configs/config_front_end.py b/compiler/tests/configs/config_front_end.py index 4486b077..2b42a914 100644 --- a/compiler/tests/configs/config_front_end.py +++ b/compiler/tests/configs/config_front_end.py @@ -11,3 +11,5 @@ num_words = 16 tech_name = OPTS.tech_name +output_name = "sram" + diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.v b/compiler/tests/golden/sram_2_16_1_freepdk45.v index 859d1cc6..da4625a9 100644 --- a/compiler/tests/golden/sram_2_16_1_freepdk45.v +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.v @@ -30,6 +30,8 @@ module sram_2_16_1_freepdk45( input [DATA_WIDTH-1:0] din0; output [DATA_WIDTH-1:0] dout0; + reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + reg csb0_reg; reg web0_reg; reg [ADDR_WIDTH-1:0] addr0_reg; @@ -44,13 +46,12 @@ module sram_2_16_1_freepdk45( addr0_reg = addr0; din0_reg = din0; #(T_HOLD) dout0 = 2'bx; - if ( !csb0_reg && web0_reg && VERBOSE ) + if ( !csb0_reg && web0_reg && VERBOSE ) $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); if ( !csb0_reg && !web0_reg && VERBOSE ) $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); end -reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; // Memory Write Block Port 0 // Write Operation : When web0 = 0, csb0 = 0 diff --git a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v index ce3714b2..0d25d63b 100644 --- a/compiler/tests/golden/sram_2_16_1_scn4m_subm.v +++ b/compiler/tests/golden/sram_2_16_1_scn4m_subm.v @@ -30,6 +30,8 @@ module sram_2_16_1_scn4m_subm( input [DATA_WIDTH-1:0] din0; output [DATA_WIDTH-1:0] dout0; + reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + reg csb0_reg; reg web0_reg; reg [ADDR_WIDTH-1:0] addr0_reg; @@ -44,13 +46,12 @@ module sram_2_16_1_scn4m_subm( addr0_reg = addr0; din0_reg = din0; #(T_HOLD) dout0 = 2'bx; - if ( !csb0_reg && web0_reg && VERBOSE ) + if ( !csb0_reg && web0_reg && VERBOSE ) $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); if ( !csb0_reg && !web0_reg && VERBOSE ) $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); end -reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; // Memory Write Block Port 0 // Write Operation : When web0 = 0, csb0 = 0 diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 928536a7..bd4a7aa2 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -7,7 +7,6 @@ # import unittest import sys, os, glob -sys.path.append(os.getenv("OPENRAM_HOME")) from globals import OPTS import debug import pdb @@ -24,24 +23,24 @@ class openram_test(unittest.TestCase): def tearDown(self): duration = time.time() - self.start_time print('%s: %.3fs' % (self.id(), duration)) - + def fail(self, msg): import inspect s = inspect.stack() base_filename = os.path.splitext(os.path.basename(s[2].filename))[0] - + try: OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) except: debug.error("$OPENRAM_HOME is not properly defined.", 1) - - import shutil - zip_file = "{0}/../{1}_{2}".format(OPENRAM_HOME, base_filename, os.getpid()) - debug.info(0, "Archiving failed temp files {0} to {1}".format(OPTS.openram_temp, zip_file)) - shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) - + + # import shutil + # zip_file = "{0}/../{1}_{2}".format(OPENRAM_HOME, base_filename, os.getpid()) + # debug.info(0, "Archiving failed temp files {0} to {1}".format(OPTS.openram_temp, zip_file)) + # shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) + super().fail(msg) - + def local_drc_check(self, w): self.reset() @@ -55,7 +54,7 @@ class openram_test(unittest.TestCase): self.fail("DRC failed: {}".format(w.name)) elif not OPTS.keep_temp: self.cleanup() - + def local_check(self, a, final_verification=False): self.reset() @@ -128,7 +127,7 @@ class openram_test(unittest.TestCase): def cleanup(self): """ Reset the duplicate checker and cleanup files. """ - + files = glob.glob(OPTS.openram_temp + '*') for f in files: # Only remove the files @@ -140,8 +139,8 @@ class openram_test(unittest.TestCase): Reset everything after each test. """ # Reset the static duplicate name checker for unit tests. - import hierarchy_design - hierarchy_design.hierarchy_design.name_map=[] + from base import hierarchy_design + hierarchy_design.name_map=[] def check_golden_data(self, data, golden_data, error_tolerance=1e-2): """ diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 53b2f167..0de27ba9 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -18,12 +18,10 @@ Calibre means pointing the code to the proper DRC and LVS rule files. import os -import shutil import re import debug -import utils from globals import OPTS -from run_script import run_script +from .run_script import run_script # Keep track of statistics num_drc_runs = 0 diff --git a/compiler/verify/klayout.py b/compiler/verify/klayout.py index b5e1fb82..73cec474 100644 --- a/compiler/verify/klayout.py +++ b/compiler/verify/klayout.py @@ -16,7 +16,7 @@ import re import shutil import debug from globals import OPTS -from run_script import * +from .run_script import * # Keep track of statistics num_drc_runs = 0 @@ -41,6 +41,16 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa else: debug.warning("Could not locate file: {}".format(full_drc_file)) + # Copy .gds file into the output directory + if os.path.isabs(gds_name): + shutil.copy(gds_name, output_path) + gds_name = os.path.basename(gds_name) + + # Copy .sp file into the output directory + if sp_name and os.path.isabs(sp_name): + shutil.copy(sp_name, output_path) + sp_name = os.path.basename(sp_name) + # Create an auxiliary script to run calibre with the runset run_file = output_path + "run_drc.sh" f = open(run_file, "w") @@ -111,14 +121,29 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out else: debug.warning("Could not locate file: {}".format(full_lvs_file)) + # Copy .gds file into the output directory + if os.path.isabs(gds_name): + shutil.copy(gds_name, output_path) + gds_name = os.path.basename(gds_name) + + # Copy .sp file into the output directory + if os.path.isabs(sp_name): + shutil.copy(sp_name, output_path) + sp_name = os.path.basename(sp_name) + run_file = output_path + "/run_lvs.sh" f = open(run_file, "w") f.write("#!/bin/sh\n") - cmd = "{0} -b -r {1} -rd input={2} -rd report={4}.lvs.report -rd schematic={3} -rd target_netlist={4}.spice".format(OPTS.lvs_exe[1], - lvs_file, - gds_name, - sp_name, - cell_name) + if final_verification: + connect_supplies = "" + else: + connect_supplies = "-rd connect_supplies=1" + cmd = "{0} -b -r {1} -rd input={2} -rd report={4}.lvs.report -rd schematic={3} -rd target_netlist={4}.spice {5}".format(OPTS.lvs_exe[1], + lvs_file, + gds_name, + sp_name, + cell_name, + connect_supplies) f.write(cmd) f.write("\n") f.close() diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 8c07df8b..e3cd8515 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -25,7 +25,7 @@ import re import shutil import debug from globals import OPTS -from run_script import * +from .run_script import * # Keep track of statistics num_drc_runs = 0 @@ -94,6 +94,9 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write("set SUB gnd\n") #f.write("gds polygon subcell true\n") f.write("gds warning default\n") + # Flatten the transistors + # Bug in Netgen 1.5.194 when using this... + f.write("gds flatglob *_?mos_m*\n") # These two options are temporarily disabled until Tim fixes a bug in magic related # to flattening channel routes and vias (hierarchy with no devices in it). Otherwise, # they appear to be disconnected. @@ -116,12 +119,14 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa pre = "#" else: pre = "" - if final_verification and OPTS.route_supplies: - f.write(pre + "extract unique all\n") # Hack to work around unit scales in SkyWater if OPTS.tech_name=="sky130": f.write(pre + "extract style ngspice(si)\n") - f.write(pre + "extract\n") + if final_verification and OPTS.route_supplies: + f.write(pre + "extract unique all\n") + f.write(pre + "extract all\n") + f.write(pre + "select top cell\n") + f.write(pre + "feedback why\n") f.write('puts "Finished extract"\n') # f.write(pre + "ext2spice hierarchy on\n") # f.write(pre + "ext2spice scale off\n") @@ -141,6 +146,8 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa # but they all seem compatible enough. f.write(pre + "ext2spice format ngspice\n") f.write(pre + "ext2spice {}\n".format(cell_name)) + f.write(pre + "select top cell\n") + f.write(pre + "feedback why\n") f.write('puts "Finished ext2spice"\n') f.write("quit -noprompt\n") @@ -260,6 +267,11 @@ def write_lvs_script(cell_name, gds_name, sp_name, final_verification=False, out if os.path.exists(full_setup_file): # Copy setup.tcl file into temp dir shutil.copy(full_setup_file, output_path) + + setup_file_object = open(output_path + "/setup.tcl", 'a') + setup_file_object.write("# Increase the column sizes for ease of reading long names\n") + setup_file_object.write("::netgen::format 120\n") + else: setup_file = 'nosetup' @@ -337,16 +349,33 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False, output_path= # Netlists match uniquely. test = re.compile("match uniquely.") - correct = list(filter(test.search, final_results)) - # Fail if they don't match. Something went wrong! - if len(correct) == 0: + uniquely = list(filter(test.search, final_results)) + + # Netlists match uniquely. + test = re.compile("match correctly.") + correctly = list(filter(test.search, final_results)) + + # Top level pins mismatch + test = re.compile("The top level cell failed pin matching.") + pins_incorrectly = list(filter(test.search, final_results)) + + # Fail if the pins mismatched + if len(pins_incorrectly) > 0: total_errors += 1 + # Fail if they don't match. Something went wrong! + if len(uniquely) == 0 and len(correctly) == 0: + total_errors += 1 + + if len(uniquely) == 0 and len(correctly) > 0: + debug.warning("{0}\tLVS matches but not uniquely".format(cell_name)) + if total_errors>0: # Just print out the whole file, it is short. for e in results: debug.info(1,e.strip("\n")) - debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name,resultsfile)) + debug.error("{0}\tLVS mismatch (results in {1})".format(cell_name, + resultsfile)) else: debug.info(1, "{0}\tLVS matches".format(cell_name)) diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..8a9a9d79 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,161 @@ +FROM ubuntu:20.04 + +ENV DEBIAN_FRONTEND noninteractive +RUN ln -s /usr/share/zoneinfo/America/Los_Angeles /etc/localtime +RUN echo "America/Los_Angeles" > /etc/timezone + +RUN apt-get update +RUN apt-get --no-install-recommends -y upgrade + +### Dependencies ### +# General tools for building etc. +RUN apt-get install --no-install-recommends -y build-essential git ssh vim gosu autoconf automake libtool bison flex +# Use bash instead of dash +RUN rm /bin/sh && ln -s /bin/bash /bin/sh +# Needed by OpenRAM +RUN apt-get install --no-install-recommends -y python3 python3-numpy python3-scipy python3-pip python3-matplotlib python3-venv python3-sklearn python3-subunit python3-coverage +# Needed by Netgen +RUN apt-get install --no-install-recommends -y m4 csh tk tk-dev tcl-dev +# Needed by ngspice +RUN apt-get install --no-install-recommends -y libxaw7-dev libreadline8 libreadline-dev +# X11 +RUN apt-get install --no-install-recommends -y libx11-dev libcairo2-dev +# Klayout +RUN apt-get install --no-install-recommends -y qt5-default qtcreator ruby-full ruby-dev python3-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 libqt5svg5-dev libqt5designer5 libqt5designercomponents5 libqt5xmlpatterns5-dev qttools5-dev + +### Klayout ### +#ARG KLAYOUT_COMMIT=v0.27.8 +ARG KLAYOUT_COMMIT=ea1bf40a1ee1c1c934e47a0020417503ab3d7e7e +WORKDIR /root +RUN git clone https://github.com/KLayout/klayout +WORKDIR /root/klayout +RUN git checkout ${KLAYOUT_COMMIT} +RUN ./build.sh -qt5 -debug -j 8 \ + && cp -r bin-debug /usr/local/klayout +RUN rm -rf /root/klayout + +### Trilinos ### +ARG TRILINOS_COMMIT=trilinos-release-12-12-1 +WORKDIR /root +RUN apt-get update +RUN apt-get install --no-install-recommends -y cmake libfftw3-dev mpich libblas-dev liblapack-dev libsuitesparse-dev libfl-dev openmpi-bin libopenmpi-dev gfortran +RUN git clone --depth 1 --branch ${TRILINOS_COMMIT} https://github.com/trilinos/Trilinos.git +RUN mkdir /root/Trilinos/build +WORKDIR /root/Trilinos/build +RUN cmake \ + -G "Unix Makefiles" \ + -DCMAKE_C_COMPILER=mpicc \ + -DCMAKE_CXX_COMPILER=mpic++ \ + -DCMAKE_Fortran_COMPILER=mpif77 \ + -DCMAKE_CXX_FLAGS="-O3 -fPIC" \ + -DCMAKE_C_FLAGS="-O3 -fPIC" \ + -DCMAKE_Fortran_FLAGS="-O3 -fPIC" \ + -DCMAKE_INSTALL_PREFIX=/usr/local/XyceLibs/Parallel \ + -DCMAKE_MAKE_PROGRAM="make" \ + -DTrilinos_ENABLE_NOX=ON \ + -DNOX_ENABLE_LOCA=ON \ + -DTrilinos_ENABLE_EpetraExt=ON \ + -DEpetraExt_BUILD_BTF=ON \ + -DEpetraExt_BUILD_EXPERIMENTAL=ON \ + -DEpetraExt_BUILD_GRAPH_REORDERINGS=ON \ + -DTrilinos_ENABLE_TrilinosCouplings=ON \ + -DTrilinos_ENABLE_Ifpack=ON \ + -DTrilinos_ENABLE_ShyLU=ON \ + -DTrilinos_ENABLE_Isorropia=ON \ + -DTrilinos_ENABLE_AztecOO=ON \ + -DTrilinos_ENABLE_Belos=ON \ + -DTrilinos_ENABLE_Teuchos=ON \ + -DTeuchos_ENABLE_COMPLEX=ON \ + -DTrilinos_ENABLE_Amesos=ON \ + -DAmesos_ENABLE_KLU=ON \ + -DTrilinos_ENABLE_Sacado=ON \ + -DTrilinos_ENABLE_Kokkos=ON \ + -DTrilinos_ENABLE_Zoltan=ON \ + -DTrilinos_ENABLE_ALL_OPTIONAL_PACKAGES=OFF \ + -DTrilinos_ENABLE_CXX11=ON \ + -DTPL_ENABLE_AMD=ON \ + -DAMD_LIBRARY_DIRS="/usr/lib" \ + -DTPL_AMD_INCLUDE_DIRS="/usr/include/suitesparse" \ + -DTPL_ENABLE_BLAS=ON \ + -DTPL_ENABLE_LAPACK=ON \ + -DTPL_ENABLE_MPI=ON \ + /root/Trilinos +RUN make -j 4 +RUN make install + +ARG XYCE_COMMIT=b7bb12d81f11d8b50141262537299b09d64b5565 +WORKDIR /root +RUN git clone https://github.com/Xyce/Xyce.git +WORKDIR /root/Xyce +RUN git checkout ${XYCE_COMMIT} +RUN ./bootstrap +RUN mkdir /root/Xyce/build +WORKDIR /root/Xyce/build +RUN ../configure CXXFLAGS="-O3 -std=c++11" \ + ARCHDIR="/usr/local/XyceLibs/Parallel" \ + CPPFLAGS="-I/usr/include/suitesparse" \ + --enable-mpi CXX=mpicxx CC=mpicc F77=mpif77 \ + --prefix=/usr/local/Xyce/Parallel --enable-shared --enable-xyce-shareable +RUN make -j 4 install + +### Ngspice ### +ARG NGSPICE_COMIT=032b1c32c4dbad45ff132bcfac1dbecadbd8abb0 +WORKDIR /root +RUN git clone git://git.code.sf.net/p/ngspice/ngspice +WORKDIR /root/ngspice +RUN git checkout ${NGSPICE_COMMIT} +RUN ./autogen.sh \ + && ./configure --enable-openmp --with-readline \ + && make \ + && make install +RUN rm -rf /root/ngspice + +### Netgen ### +#ARG NETGEN_COMMIT=1.5.195 +ARG NETGEN_COMMIT=1.5.221 +WORKDIR /root +#RUN git clone https://github.com/RTimothyEdwards/netgen.git netgen +RUN git clone git://opencircuitdesign.com/netgen netgen +WORKDIR /root/netgen +RUN git checkout ${NETGEN_COMMIT} +RUN ./configure \ + && make -j$(nproc) \ + && make install +RUN rm -rf /root/netgen + +### iVerilog ### +RUN apt-get install --no-install-recommends -y iverilog + +### Magic ### +#ARG MAGIC_COMMIT=db4fa65bfc096e63954b37b188ea27b90ab31839 +#ARG MAGIC_COMMIT=8.3.274 +ARG MAGIC_COMMIT=8.3.311 +WORKDIR /root +#RUN git clone https://github.com/RTimothyEdwards/magic.git magic +RUN git clone git://opencircuitdesign.com/magic magic +WORKDIR /root/magic +RUN git checkout ${MAGIC_COMMIT} +COPY mrg.patch /root/magic +RUN git apply mrg.patch +RUN ./configure \ + && make \ + && make install +RUN rm -rf /root/magic + + +### CLEAN UP ### +# Remove development tools to save space +RUN apt-get remove -y build-essential autoconf automake libtool bison flex tcl-dev tk-dev cmake +# Cleanup to save some space +RUN apt-get clean +RUN rm -rf /var/lib/apt/lists/* + + +# ### SET UP A GENERIC USER ### +RUN useradd cad-user +RUN mkdir /home/cad-user +RUN chown -R cad-user /home/cad-user +RUN chgrp -R cad-user /home/cad-user +ADD set-paths.sh /home/cad-user/.bashrc +USER cad-user +WORKDIR /home/cad-user diff --git a/docker/Makefile b/docker/Makefile new file mode 100644 index 00000000..0e6a0143 --- /dev/null +++ b/docker/Makefile @@ -0,0 +1,21 @@ +DOCKER_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) +TOP_DIR := $(realpath $(DOCKER_DIR)/..) +include $(TOP_DIR)/openram.mk + +TAG_DATE := $(shell date +%F) +all: build push + +.PHONY: build +build: + docker build -t vlsida/openram-ubuntu:${TAG_DATE} -f Dockerfile . | tee -i openram-ubuntu.log + docker tag vlsida/openram-ubuntu:${TAG_DATE} vlsida/openram-ubuntu:latest + +.PHONY: push +push: + docker login + docker push vlsida/openram-ubuntu:latest + +.PHONY: pull +pull: + docker pull vlsida/openram-ubuntu:latest + diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..34b25ff9 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,47 @@ +# Docker images for OpenRAM # + +## Installing Docker ## + +There are a number of ways to install Docker. Pick your favorite. + +* On Mac from docker.com with .app: + https://docs.docker.com/docker-for-mac/install/ + +* On Windows from docker.com: + https://docs.docker.com/docker-for-windows/install/ + +* On Ubuntu: + https://docs.docker.com/install/linux/docker-ce/ubuntu/ + +NOTE: If you plan to use a VPN, do *NOT* use the Docker Toolbox for +Mac or the docker from [Macports](https://www.macports.org/ +"Macports") as these require a network socket that breaks when you +install some VPN software. To understand the difference, check out [this +page](https://docs.docker.com/docker-for-mac/docker-toolbox/). + +## Running Docker ## + +### Terminal only ### + +* To run as a generic user: +``` +make mount +``` + +## Updating the image ## + +If there are updates to the image, you can pull a new one from the hub with: +``` +make pull +``` +This is not automatically done, so if you have a problem, make sure you are up-to-date. + +## Building your own image ## + +You can run the build script to build a local image: + +``` +make build +``` + +If you want to change things, modify the openram-ubuntu/Dockerfile and let me know what should be fixed. diff --git a/docker/mrg.patch b/docker/mrg.patch new file mode 100644 index 00000000..01b38f96 --- /dev/null +++ b/docker/mrg.patch @@ -0,0 +1,22 @@ +diff --git a/commands/CmdLQ.c b/commands/CmdLQ.c +index ca70086..ee3242c 100644 +--- a/commands/CmdLQ.c ++++ b/commands/CmdLQ.c +@@ -1745,7 +1745,7 @@ CmdPort(w, cmd) + if (sl->lab_flags & PORT_DIR_MASK) + { + idx = (int)sl->lab_port; +- if (idx < i) i = idx; ++ if (idx < i || i == -1) i = idx; + } + } + #ifdef MAGIC_WRAPPER +@@ -1764,7 +1764,7 @@ CmdPort(w, cmd) + { + idx = (int)sl->lab_port; + if (idx > refidx) +- if (idx < i) i = idx; ++ if (idx < i || i == -1) i = idx; + } + } + #ifdef MAGIC_WRAPPER diff --git a/docker/set-paths.sh b/docker/set-paths.sh new file mode 100644 index 00000000..1435f7cb --- /dev/null +++ b/docker/set-paths.sh @@ -0,0 +1,15 @@ + +export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin + +# Klayout +export KLAYOUT_HOME=/usr/local/klayout +export PATH=$PATH:$KLAYOUT_HOME +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$KLAYOUT_HOME + +# Xyce +export XYCE_HOME=/usr/local/Xyce/Parallel +export XYCE_PATH=$XYCE_HOME/bin +export PATH=$PATH:$XYCE_PATH +export XYCE_LIB=$XYCE_HOME/lib +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$XYCE_LIB +export XYCE_NO_TRACKING="anything at all" diff --git a/macros/Makefile b/macros/Makefile new file mode 100644 index 00000000..630541c0 --- /dev/null +++ b/macros/Makefile @@ -0,0 +1,80 @@ +MACRO_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) +TOP_DIR := $(realpath $(MACRO_DIR)/..) +include $(TOP_DIR)/openram.mk + +.DEFAULT_GOAL := all + +SKY130_PDK ?= $(PDK_ROOT)/sky130A + +OPENRAM_OPTS := $(OPENRAM_OPTS) +# Define `OPENRAM_FULL` in your environment to run a full characterize +ifeq ($(OPENRAM_FULL),) +# Do not characterize (default) +else +# Characterize +OPTS += -c +endif +# Verbosity +OPENRAM_OPTS += -v --keeptemp + +CONFIG_DIR = configs +SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(CONFIG_DIR)/*.py)))) +DIRS=$(basename $(SRCS)) +STAMPS=$(addsuffix .ok,$(DIRS)) + +configs: + @echo + @echo "Using OpenRAM at $(OPENRAM_HOME)" + @echo " (which is version $$(cd $(OPENRAM_HOME); git describe --tags))" + @echo + @echo "Configurations:" + @for D in $(DIRS); do echo " - $$D"; done + @echo + +.PHONY: configs + +BROKEN := + +WORKING_STAMPS=$(filter-out $(addsuffix .ok, $(BROKEN)), $(STAMPS)) +EXAMPLE_STAMPS=$(filter example%, $(WORKING_STAMPS)) +SKY130_STAMPS=$(filter sky130%, $(WORKING_STAMPS)) +FREEPDK45_STAMPS=$(filter freepdk45%, $(WORKING_STAMPS)) +SCN4M_SUBM_STAMPS=$(filter scn4m_subm%, $(WORKING_STAMPS)) + +all: | configs + @echo + @echo "Building following working configs" + @for S in $(WORKING_STAMPS); do echo " - $$S"; done + @sleep 5 + $(MAKE) $(WORKING_STAMPS) + @echo "Built all macros." + +example: $(EXAMPLE_STAMPS) +.PHONY: example + +sky130: $(SKY130_STAMPS) +.PHONY: sky130 + +freepdk45: $(FREEPDK45_STAMPS) +.PHONY: freepdk45 + +scn4m_subm: $(SCN4M_SUBM_STAMPS) +.PHONY: scn4m_subm + +OPENRAM_TMP=/openram/macros/$*/tmp +%.ok: configs/%.py + @echo "Building $*" + @mkdir -p $* + @$(DOCKER_CMD) python3 -u /openram/compiler/openram.py $(OPENRAM_OPTS) -o $* -p /openram/macros/$* /openram/macros/$< && touch $@ + +.DELETE_ON_ERROR: $(STAMPS) + +$(DIRS): + @$(MAKE) --no-print-directory $@.ok + +.PHONY: $(DIRS) + +clean: + rm -rf $(STAMPS) + rm -rf $(DIRS) +.PHONY: clean diff --git a/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py b/macros/configs/example_config_1rw_1r_scn4m_subm.py similarity index 100% rename from compiler/example_configs/example_config_1rw_1r_scn4m_subm.py rename to macros/configs/example_config_1rw_1r_scn4m_subm.py diff --git a/compiler/example_configs/example_config_1rw_1w_scn4m_subm.py b/macros/configs/example_config_1rw_1w_scn4m_subm.py similarity index 100% rename from compiler/example_configs/example_config_1rw_1w_scn4m_subm.py rename to macros/configs/example_config_1rw_1w_scn4m_subm.py diff --git a/compiler/example_configs/example_config_1rw_2mux_scn4m_subm.py b/macros/configs/example_config_1rw_2mux_scn4m_subm.py similarity index 100% rename from compiler/example_configs/example_config_1rw_2mux_scn4m_subm.py rename to macros/configs/example_config_1rw_2mux_scn4m_subm.py diff --git a/compiler/example_configs/example_config_1w_1r_scn4m_subm.py b/macros/configs/example_config_1w_1r_scn4m_subm.py similarity index 100% rename from compiler/example_configs/example_config_1w_1r_scn4m_subm.py rename to macros/configs/example_config_1w_1r_scn4m_subm.py diff --git a/compiler/example_configs/example_config_2rw_scn4m_subm.py b/macros/configs/example_config_2rw_scn4m_subm.py similarity index 100% rename from compiler/example_configs/example_config_2rw_scn4m_subm.py rename to macros/configs/example_config_2rw_scn4m_subm.py diff --git a/compiler/example_configs/big_config_scn4m_subm.py b/macros/configs/example_config_big_scn4m_subm.py similarity index 100% rename from compiler/example_configs/big_config_scn4m_subm.py rename to macros/configs/example_config_big_scn4m_subm.py diff --git a/compiler/example_configs/example_config_freepdk45.py b/macros/configs/example_config_freepdk45.py similarity index 100% rename from compiler/example_configs/example_config_freepdk45.py rename to macros/configs/example_config_freepdk45.py diff --git a/compiler/example_configs/giant_config_scn4m_subm.py b/macros/configs/example_config_giant_scn4m_subm.py similarity index 100% rename from compiler/example_configs/giant_config_scn4m_subm.py rename to macros/configs/example_config_giant_scn4m_subm.py diff --git a/compiler/example_configs/medium_config_scn4m_subm.py b/macros/configs/example_config_medium_scn4m_subm.py similarity index 100% rename from compiler/example_configs/medium_config_scn4m_subm.py rename to macros/configs/example_config_medium_scn4m_subm.py diff --git a/compiler/example_configs/example_config_scn4m_subm.py b/macros/configs/example_config_scn4m_subm.py similarity index 100% rename from compiler/example_configs/example_config_scn4m_subm.py rename to macros/configs/example_config_scn4m_subm.py diff --git a/compiler/example_configs/riscv_freepdk45_8kbyte.py b/macros/configs/freepdk45_sram_1rw1r_32x2048_8.py similarity index 100% rename from compiler/example_configs/riscv_freepdk45_8kbyte.py rename to macros/configs/freepdk45_sram_1rw1r_32x2048_8.py diff --git a/compiler/example_configs/riscv_scn4m_subm_16kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py similarity index 100% rename from compiler/example_configs/riscv_scn4m_subm_16kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_16kbyte_1rw1r_32x4096_8.py diff --git a/compiler/example_configs/riscv_scn4m_subm_1kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py similarity index 100% rename from compiler/example_configs/riscv_scn4m_subm_1kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_1kbyte_1rw1r_32x256_8.py diff --git a/compiler/example_configs/riscv_scn4m_subm_2kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py similarity index 100% rename from compiler/example_configs/riscv_scn4m_subm_2kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_2kbyte_1rw1r_32x512_8.py diff --git a/compiler/example_configs/riscv_scn4m_subm_32kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py similarity index 98% rename from compiler/example_configs/riscv_scn4m_subm_32kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py index 87ddb5eb..285b1dbf 100644 --- a/compiler/example_configs/riscv_scn4m_subm_32kbyte_1rw1r.py +++ b/macros/configs/scn4m_subm_sram_32kbyte_1rw1r_2x32x4096_8.py @@ -1,3 +1,4 @@ +num_banks=2 word_size = 32 num_words = 8192 write_size = 8 diff --git a/compiler/example_configs/riscv_scn4m_subm_4kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py similarity index 100% rename from compiler/example_configs/riscv_scn4m_subm_4kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_4kbyte_1rw1r_32x1024_8.py diff --git a/compiler/example_configs/riscv_scn4m_subm_8kbyte_1rw1r.py b/macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py similarity index 100% rename from compiler/example_configs/riscv_scn4m_subm_8kbyte_1rw1r.py rename to macros/configs/scn4m_subm_sram_8kbyte_1rw1r_32x2048_8.py diff --git a/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py b/macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py similarity index 95% rename from compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py rename to macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py index 6462032e..5d86dff6 100644 --- a/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py +++ b/macros/configs/sky130_sram_1kbyte_1r1w_8x1024_8.py @@ -8,7 +8,7 @@ num_words = 1024 human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) # Allow byte writes -#write_size = 8 # Bits +write_size = 8 # Bits # Dual port num_rw_ports = 0 diff --git a/compiler/example_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py b/macros/configs/sky130_sram_1kbyte_1rw1r_32x256_8.py similarity index 100% rename from compiler/example_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py rename to macros/configs/sky130_sram_1kbyte_1rw1r_32x256_8.py diff --git a/compiler/example_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py b/macros/configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py similarity index 100% rename from compiler/example_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py rename to macros/configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py diff --git a/compiler/example_configs/sky130_sram_1kbyte_1rw_32x256_8.py b/macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py similarity index 91% rename from compiler/example_configs/sky130_sram_1kbyte_1rw_32x256_8.py rename to macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py index 955d3959..88bbfd6f 100644 --- a/compiler/example_configs/sky130_sram_1kbyte_1rw_32x256_8.py +++ b/macros/configs/sky130_sram_1kbyte_1rw_32x256_8.py @@ -13,6 +13,8 @@ write_size = 8 # Bits num_rw_ports = 1 num_r_ports = 0 num_w_ports = 0 +num_spare_rows = 1 +num_spare_cols = 1 ports_human = '1rw' import os diff --git a/macros/configs/sky130_sram_1rw1r_tiny.py b/macros/configs/sky130_sram_1rw1r_tiny.py new file mode 100644 index 00000000..95bfd61f --- /dev/null +++ b/macros/configs/sky130_sram_1rw1r_tiny.py @@ -0,0 +1,21 @@ +""" +Dual port (1 read/write + 1 read only) 1 kbytes SRAM with byte write. + +FIXME: What is this useful for? +FIXME: Why would you want byte write on this? +""" +word_size = 8 # Bits +num_words = 16 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 2 # Bits + +# Dual port +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 +ports_human = '1rw1r' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/macros/configs/sky130_sram_1rw_tiny.py b/macros/configs/sky130_sram_1rw_tiny.py new file mode 100644 index 00000000..85aa34f4 --- /dev/null +++ b/macros/configs/sky130_sram_1rw_tiny.py @@ -0,0 +1,24 @@ +""" +Dual port (1 read/write + 1 read only) 1 kbytes SRAM with byte write. + +FIXME: What is this useful for? +FIXME: Why would you want byte write on this? +""" +word_size = 8 # Bits +num_words = 16 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 2 # Bits + +# Dual port +num_rw_ports = 1 +num_r_ports = 0 +num_w_ports = 0 +ports_human = '1rw' + +num_spare_cols = 1 +num_spare_rows = 1 + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py b/macros/configs/sky130_sram_2kbyte_1rw1r_32x512_8.py similarity index 100% rename from compiler/example_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py rename to macros/configs/sky130_sram_2kbyte_1rw1r_32x512_8.py diff --git a/compiler/example_configs/sky130_sram_2kbyte_1rw_32x512_8.py b/macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py similarity index 91% rename from compiler/example_configs/sky130_sram_2kbyte_1rw_32x512_8.py rename to macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py index 7f64d18c..8e2be639 100644 --- a/compiler/example_configs/sky130_sram_2kbyte_1rw_32x512_8.py +++ b/macros/configs/sky130_sram_2kbyte_1rw_32x512_8.py @@ -13,6 +13,8 @@ write_size = 8 # Bits num_rw_ports = 1 num_r_ports = 0 num_w_ports = 0 +num_spare_rows = 1 +num_spare_cols = 1 ports_human = '1rw' import os diff --git a/compiler/example_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py b/macros/configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py similarity index 100% rename from compiler/example_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py rename to macros/configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py diff --git a/compiler/example_configs/sky130_sram_4kbyte_1rw_32x1024_8.py b/macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py similarity index 91% rename from compiler/example_configs/sky130_sram_4kbyte_1rw_32x1024_8.py rename to macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py index 571ca030..50c01e92 100644 --- a/compiler/example_configs/sky130_sram_4kbyte_1rw_32x1024_8.py +++ b/macros/configs/sky130_sram_4kbyte_1rw_32x1024_8.py @@ -14,6 +14,8 @@ write_size = 8 # Bits num_rw_ports = 1 num_r_ports = 0 num_w_ports = 0 +num_spare_rows = 1 +num_spare_cols = 1 ports_human = '1rw' import os diff --git a/macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py b/macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py new file mode 100644 index 00000000..34ae8537 --- /dev/null +++ b/macros/configs/sky130_sram_4kbyte_1rw_64x512_8.py @@ -0,0 +1,21 @@ +""" +Single port, 1 kbytes SRAM, with byte write, useful for RISC-V processor main +memory. +""" +word_size = 64 # Bits +num_words = 512 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Single port +num_rw_ports = 1 +num_r_ports = 0 +num_w_ports = 0 +num_spare_rows = 1 +num_spare_cols = 1 +ports_human = '1rw' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_common.py b/macros/configs/sky130_sram_common.py similarity index 100% rename from compiler/example_configs/sky130_sram_common.py rename to macros/configs/sky130_sram_common.py diff --git a/openram.mk b/openram.mk new file mode 100644 index 00000000..f169fa32 --- /dev/null +++ b/openram.mk @@ -0,0 +1,51 @@ +OPENRAM_HOME := $(abspath $(TOP_DIR)/compiler) +OPENRAM_TECH := $(abspath $(TOP_DIR)/technology) +OPENRAM_COMPILER := $(OPENRAM_HOME)/openram.py + +PDK_ROOT ?= $(TOP_DIR) + +ifeq (,$(wildcard $(OPENRAM_COMPILER))) +$(error Did not find '$(OPENRAM_COMPILER)' in '$(OPENRAM_HOME)' (from $$OPENRAM_HOME)) +endif +export OPENRAM_HOME +export OPENRAM_TECH +export PDK_ROOT +#$(info Using OPENRAM_HOME=$(OPENRAM_HOME)) +#$(info Using OPENRAM_TECH=$(OPENRAM_TECH)) +#$(info Using PDK_ROOT=$(PDK_ROOT)) + +UID = $(shell id -u) +GID = $(shell id -g) + +export OPENRAM_TMP=$(OPENRAM_DIR)/results/$*/tmp +export DOCKER_CMD= docker run \ + -v $(TOP_DIR):/openram \ + -v $(FREEPDK45):/freepdk45 \ + -e FREEPDK45=/freepdk45 \ + -v $(PDK_ROOT):/pdk \ + -e PDK_ROOT=/pdk \ + -e PDKPATH=/pdk/sky130A \ + -e OPENRAM_HOME=/openram/compiler \ + -e OPENRAM_TECH=/openram/technology \ + -e OPENRAM_TMP=$(OPENRAM_TMP)\ + -e PYTHONPATH=/openram/compiler \ + -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro \ + --user $(UID):$(GID) \ + vlsida/openram-ubuntu:latest + +mount: + @docker run -it \ + -v $(TOP_DIR):/openram \ + -v $(FREEPDK45):/freepdk45 \ + -e FREEPDK45=/freepdk45 \ + -v $(PDK_ROOT):/pdk \ + -e PDK_ROOT=/pdk \ + -e PDKPATH=/pdk/sky130A \ + -e OPENRAM_HOME=/openram/compiler \ + -e OPENRAM_TECH=/openram/technology \ + -e PYTHONPATH=/openram/compiler \ + -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro \ + --user $(UID):$(GID) \ + vlsida/openram-ubuntu:latest +.PHONY: mount + diff --git a/setpaths.csh b/setpaths.csh deleted file mode 100755 index 69f736d4..00000000 --- a/setpaths.csh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/csh -# This is a csh utility script to set the paths to the current -# directory of OpenRAM. It must be sourced in the local directory -# like this: -# source setpaths.csh - -setenv OPENRAM_HOME "`pwd`/compiler" -setenv OPENRAM_TECH "`pwd`/technology" diff --git a/setpaths.sh b/setpaths.sh index 44485f14..280594f5 100755 --- a/setpaths.sh +++ b/setpaths.sh @@ -6,3 +6,10 @@ export OPENRAM_HOME="`pwd`/compiler" export OPENRAM_TECH="`pwd`/technology" +export PYTHONPATH=$OPENRAM_HOME +for dir in `pwd`/compiler/* +do + if [ -d $dir ]; then + export PYTHONPATH=$PYTHONPATH:$dir + fi; +done diff --git a/technology/freepdk45/__init__.py b/technology/freepdk45/__init__.py index b86b17be..6d9e759d 100644 --- a/technology/freepdk45/__init__.py +++ b/technology/freepdk45/__init__.py @@ -13,13 +13,17 @@ the trunk import sys import os +import debug TECHNOLOGY = "freepdk45" ########################## # FreePDK45 paths -PDK_DIR=os.path.abspath(os.environ.get("FREEPDK45")) +PDK_PATH=os.environ.get("FREEPDK45") +if PDK_PATH==None: + debug.error("Must define FREEPDK45 to point to PDK.", -1) +PDK_DIR=os.path.abspath(PDK_PATH) os.environ["PDK_DIR"] = PDK_DIR os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/ncsu_basekit/cdssetup".format(PDK_DIR) os.environ["CDS_SITE"] = PDK_DIR diff --git a/technology/freepdk45/gds_lib/cell_1rw.gds b/technology/freepdk45/gds_lib/cell_1rw.gds index 3a67e40b..e8116b9e 100644 Binary files a/technology/freepdk45/gds_lib/cell_1rw.gds and b/technology/freepdk45/gds_lib/cell_1rw.gds differ diff --git a/technology/freepdk45/gds_lib/cell_2rw.gds b/technology/freepdk45/gds_lib/cell_2rw.gds index 3ceea617..728b20a7 100644 Binary files a/technology/freepdk45/gds_lib/cell_2rw.gds and b/technology/freepdk45/gds_lib/cell_2rw.gds differ diff --git a/technology/freepdk45/gds_lib/dummy_cell_1rw.gds b/technology/freepdk45/gds_lib/dummy_cell_1rw.gds index cbfcb064..92d2b130 100644 Binary files a/technology/freepdk45/gds_lib/dummy_cell_1rw.gds and b/technology/freepdk45/gds_lib/dummy_cell_1rw.gds differ diff --git a/technology/freepdk45/gds_lib/dummy_cell_2rw.gds b/technology/freepdk45/gds_lib/dummy_cell_2rw.gds index f5a4600f..1c08cb4f 100644 Binary files a/technology/freepdk45/gds_lib/dummy_cell_2rw.gds and b/technology/freepdk45/gds_lib/dummy_cell_2rw.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_1rw.gds b/technology/freepdk45/gds_lib/replica_cell_1rw.gds index cef0722d..076a8049 100644 Binary files a/technology/freepdk45/gds_lib/replica_cell_1rw.gds and b/technology/freepdk45/gds_lib/replica_cell_1rw.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_2rw.gds b/technology/freepdk45/gds_lib/replica_cell_2rw.gds index 1b2564a4..f7bec774 100644 Binary files a/technology/freepdk45/gds_lib/replica_cell_2rw.gds and b/technology/freepdk45/gds_lib/replica_cell_2rw.gds differ diff --git a/technology/freepdk45/gds_lib/sense_amp.gds b/technology/freepdk45/gds_lib/sense_amp.gds index edff18ab..f0bfebfd 100644 Binary files a/technology/freepdk45/gds_lib/sense_amp.gds and b/technology/freepdk45/gds_lib/sense_amp.gds differ diff --git a/technology/freepdk45/tech/freepdk45.lydrc b/technology/freepdk45/tech/freepdk45.lydrc index 91c7c30f..0c380c50 100644 --- a/technology/freepdk45/tech/freepdk45.lydrc +++ b/technology/freepdk45/tech/freepdk45.lydrc @@ -110,8 +110,9 @@ end # Wells nwell.and(pwell).output("WELL.1", "WELL.1 : nwell/pwell must not overlap") # the rule "WELL.2 : Minimum spacing of well at different potential : 225nm" was not coded : see : https://www.klayout.de/forum/discussion/comment/6021 -nwell.space(135.nm, euclidian).output("WELL.3", "WELL.3 : Minimum spacing of nwell at same potential : 135nm") -pwell.space(135.nm, euclidian).output("WELL.3", "WELL.3 : Minimum spacing of pwell at same potential : 135nm") +# the rule WELL.3 was not detected in the original FreePDK45 rule deck +#nwell.space(135.nm, euclidian).output("WELL.3", "WELL.3 : Minimum spacing of nwell at same potential : 135nm") +#pwell.space(135.nm, euclidian).output("WELL.3", "WELL.3 : Minimum spacing of pwell at same potential : 135nm") well.separation(well, 200.nm, euclidian).output("WELL.4", "WELL.4 : Minimum width of nwell/pwell : 200nm") vtg.not(well).output("VT.1","VT.1 : Vtg adjust layers must coincide with well") vth.not(well).output("VT.1","VT.1 : Vth adjust layers must coincide with well") diff --git a/technology/freepdk45/tech/freepdk45.lylvs b/technology/freepdk45/tech/freepdk45.lylvs index 5d8e50b3..5500d9e3 100644 --- a/technology/freepdk45/tech/freepdk45.lylvs +++ b/technology/freepdk45/tech/freepdk45.lylvs @@ -135,7 +135,7 @@ lv_ngate = ngate - vtg - thkox gv_ngate = ngate & vtg - vth - thkox hv_ngate = ngate - vtg - vth & thkox -cheat("cell_6t", "dummy_cell_6t", "cell_1rw", "dummy_cell_1rw", "cell_2rw", "dummy_cell_2rw", "dff","wordline_driver_0") { +cheat("cell_1rw", "dummy_cell_1rw", "replica_cell_1rw", "cell_2rw", "dummy_cell_2rw", "replica_cell_2rw", "pbitcell*", "dummy_pbitcell*", "replica_pbitcell*", "dff", "wordline_driver*") { # PMOS transistor device extraction extract_devices(mos4("PMOS_VTL"), { "SD" => psd, "G" => lv_pgate, "tS" => psd, "tD" => psd, "tG" => poly, "W" => nwell }) @@ -205,14 +205,19 @@ connect(metal10, metal10_pin) # Global schematic.simplify +if $connect_supplies +connect_implicit("*", "vdd") +connect_implicit("*", "gnd") +end + connect_global(pwell, "PWELL") connect_global(nwell, "NWELL") connect_global(bulk, "BULK") -#for pat in %w(pnand*_0 and2_dec_0 port_address* replica_bitcell_array) -# connect_explicit(pat, [ "NWELL", "vdd" ]) -# connect_explicit(pat, [ "BULK", "PWELL", "gnd" ]) -#end +for pat in %w(*pinv* *pnor* *pnand* *and?_dec* *write_driver* *port_address* *replica_bitcell_array*) + connect_explicit(pat, [ "NWELL", "vdd" ]) + connect_explicit(pat, [ "BULK", "PWELL", "gnd" ]) +end #for pat in %w(XOR* XNOR* TLAT* TINV* TBUF* SDFF* OR* OAI* NOR* NAND* MUX* LOGIC* INV* HA* FILLCELL* # FA* DLL* DLH* DFF* DFFS* DFFR* DFFRS* CLKGATE* CLKBUF* BUF* AOI* ANTENNA* AND*) diff --git a/technology/freepdk45/tech/scn4m_subm.lyp b/technology/freepdk45/tech/scn4m_subm.lyp new file mode 100644 index 00000000..2aa688d3 --- /dev/null +++ b/technology/freepdk45/tech/scn4m_subm.lyp @@ -0,0 +1,2518 @@ + + + + #ff80ff + #ff80ff + 0 + 0 + C56 + + true + true + false + 2 + false + false + 0 + DeepNwell.drawing - 38/0 + 38/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C56 + + true + true + false + 2 + false + false + 0 + Nwell.drawing - 42/0 + 42/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C61 + + true + true + false + 1 + false + false + 0 + Pwell.drawing - 41/0 + 41/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C68 + + true + true + false + 1 + false + false + 0 + CapWell.drawing - 59/0 + 59/0@1 + + + #00ff00 + #00ff00 + 0 + 0 + C58 + + true + true + false + 1 + false + false + 0 + Active.drawing - 43/0 + 43/0@1 + + + #8c8ca6 + #8c8ca6 + 0 + 0 + C58 + + true + true + false + 1 + false + false + 0 + ThickActive.drawing - 60/0 + 60/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + pBase.drawing - 60/0 + 60/0@1 + + + #ff0000 + #ff0000 + 0 + 0 + I11 + + true + true + false + 1 + false + false + 0 + Poly.drawing - 46/0 + 46/0@1 + + + #ff0000 + #ff0000 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Poly.text - 46/1 + 46/1@1 + + + #008050 + #008050 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + SiBlock.drawing - 29/0 + 29/0@1 + + + #ff80a8 + #ff80a8 + 0 + 0 + C57 + + true + true + false + 1 + false + false + 0 + Nselect.drawing - 45/0 + 45/0@1 + + + #bf4026 + #bf4026 + 0 + 0 + C76 + + true + true + false + 1 + false + false + 0 + Pselect.drawing - 44/0 + 44/0@1 + + + #ff8000 + #ff8000 + 0 + 0 + C76 + + true + true + false + 1 + false + false + 0 + Poly2.drawing - 56/0 + 56/0@1 + + + #802626 + #802626 + 0 + 0 + C66 + + true + true + false + 1 + false + false + 0 + HiRes.drawing - 34/0 + 34/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Contact.drawing - 25/0 + 25/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + ContactPoly.drawing - 47/0 + 47/0@1 + + + #00cc66 + #00cc66 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + ContactActive.drawing - 48/0 + 48/0@1 + + + #0000ff + #0000ff + 0 + 0 + C39 + + true + true + false + 1 + false + false + 0 + Metal1.drawing - 49/0 + 49/0@1 + + + #0000ff + #0000ff + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal1.text - 49/1 + 49/1@1 + + + #ffff00 + #ffff00 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via.drawing - 50/0 + 50/0@1 + + + #00ffff + #00ffff + 0 + 0 + C40 + + true + true + false + 1 + false + false + 0 + Metal2.drawing - 51/0 + 51/0@1 + + + #00ffff + #00ffff + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal2.text - 51/1 + 51/1@1 + + + #d9e6ff + #d9e6ff + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via2.drawing - 61/0 + 61/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + I8 + + true + true + false + 1 + false + false + 0 + Metal3.drawing - 62/0 + 62/0@1 + + + #ffff00 + #ffff00 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal3.text - 62/1 + 62/1@1 + + + #91ff00 + #91ff00 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via3.drawing - 30/0 + 30/0@1 + + + #7a732d + #7a732d + 0 + 0 + C39 + + true + true + false + 1 + false + false + 0 + Metal4.drawing - 31/0 + 31/0@1 + + + #7a732d + #7a732d + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal4.text - 31/1 + 31/1@1 + + + #8000ff + #8000ff + 0 + 0 + I6 + + true + true + false + 1 + false + false + 0 + CapTopMetal.drawing - 35/0 + 35/0@1 + + + #008000 + #008000 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via4.drawing - 32/0 + 32/0@1 + + + #7777ff + #7777ff + 0 + 0 + I11 + + true + true + false + 1 + false + false + 0 + Metal5.drawing - 33/0 + 33/0@1 + + + #7777ff + #7777ff + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal5.text - 33/1 + 33/1@1 + + + #004080 + #004080 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via5.drawing - 36/0 + 36/0@1 + + + #ff9933 + #ff9933 + 0 + 0 + I7 + + true + true + false + 1 + false + false + 0 + Metal6.drawing - 37/0 + 37/0@1 + + + #ff9933 + #ff9933 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal6.text - 37/1 + 37/1@1 + + + #808000 + #808000 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via6.drawing - 127/0 + 127/0@1 + + + #ff7777 + #ff7777 + 0 + 0 + I11 + + true + true + false + 1 + false + false + 0 + Metal7.drawing - 126/0 + 126/0@1 + + + #ff7777 + #ff7777 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal7.text - 126/1 + 126/1@1 + + + #804080 + #804080 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via7.drawing - 129/0 + 129/0@1 + + + #3399ff + #3399ff + 0 + 0 + I7 + + true + true + false + 1 + false + false + 0 + Metal8.drawing - 53/0 + 53/0@1 + + + #3399ff + #3399ff + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal8.text - 53/1 + 53/1@1 + + + #008080 + #008080 + 0 + 0 + I1 + + true + true + false + 2 + false + true + 0 + Via8.drawing - 26/0 + 26/0@1 + + + #77ff77 + #77ff77 + 0 + 0 + I11 + + true + true + false + 1 + false + false + 0 + Metal9.drawing - 54/0 + 54/0@1 + + + #77ff77 + #77ff77 + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Metal9.text - 54/1 + 54/1@1 + + + #33aaff + #33aaff + 0 + 0 + I1 + I2 + true + true + false + 3 + false + false + 0 + Glass.drawing - 52/0 + 52/0@1 + + + #ffffff + #ffffff + 0 + 0 + I15 + + true + true + false + 3 + false + false + 0 + Pads.drawing - 26/0 + 26/0@1 + + + #ffffff + #ffffff + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Text.drawing - 63/0 + 63/0@1 + + + #ffffff + #ffffff + 0 + 0 + I1 + + true + true + false + 1 + false + false + 0 + Text.drawing - 83/0 + 83/0@1 + + + + + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + + 1 + SCst4 + + + + ................ + ....*.*.....*.*. + .....*.......*.. + ....*.*.....*.*. + ................ + .*.*.....*.*.... + .*.......*...... + .*.*.....*.*.... + ................ + ....*.*.....*.*. + .....*.......*.. + ....*.*.....*.*. + ................ + .*.*.....*.*.... + .*.......*...... + .*.*.....*.*.... + + 2 + SCst3 + + + + ................ + ....*.*.....*.*. + .....*.......*.. + ....*.*.....*.*. + ................ + *.*.....*.*..... + .*.......*...... + *.*.....*.*..... + ................ + ....*.*.....*.*. + .....*.......*.. + ....*.*.....*.*. + ................ + *.*.....*.*..... + .*.......*...... + *.*.....*.*..... + + 3 + SCst2 + + + + *.*.*.*. + .*.*.*.* + *.*.*.*. + .*.*.*.* + *.*.*.*. + .*.*.*.* + *.*.*.*. + .*.*.*.* + + 4 + SCst1 + + + + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + ....*....*....*. + + 5 + verLine + + + + ................ + **************** + ................ + ................ + ................ + ................ + **************** + ................ + ................ + ................ + ................ + **************** + ................ + ................ + ................ + ................ + + 6 + horLine + + + + ................ + ................ + ................ + ................ + ................ + ..........*..... + .........*...... + ........*....... + .......*........ + ......*......... + .....*.......... + ................ + ................ + ................ + ................ + ................ + + 7 + stipple35 + + + + ................ + ................ + ................ + ................ + ................ + .....*.......... + ......*......... + .......*........ + ........*....... + .........*...... + ..........*..... + ................ + ................ + ................ + ................ + ................ + + 8 + stipple34 + + + + ....*.......*... + ................ + ................ + ................ + *.......*....... + ................ + ................ + ................ + ....*.......*... + ................ + ................ + ................ + *.......*....... + ................ + ................ + ................ + + 9 + stipple33 + + + + *.......*....... + ................ + ................ + ................ + ....*.......*... + ................ + ................ + ................ + *.......*....... + ................ + ................ + ................ + ....*.......*... + ................ + ................ + ................ + + 10 + stipple32 + + + + ...............* + ..............*. + .............*.. + ............*... + ...........*.... + ..........*..... + .........*...... + ........*....... + .......*........ + ......*......... + .....*.......... + ....*........... + ...*............ + ..*............. + .*.............. + *............... + + 11 + stipple31 + + + + .....*.......*.. + ....*.......*... + ...*.......*.... + ..*.......*..... + .*.......*...... + *.......*....... + .......*.......* + ......*.......*. + .....*.......*.. + ....*.......*... + ...*.......*.... + ..*.......*..... + .*.......*...... + *.......*....... + .......*.......* + ......*.......*. + + 12 + stipple30 + + + + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + + 13 + stipple29 + + + + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + + 14 + stipple28 + + + + ..*....*..*....* + .**...*..**...*. + *....*..*....*.. + *...*..**...*..* + ...*..*....*..*. + ..*..**...*..**. + .*..*....*..*... + *..**...*..**... + ..*....*..*....* + .**...*..**...*. + *....*..*....*.. + *...*..**...*..* + ...*..*....*..*. + ..*..**...*..**. + .*..*....*..*... + *..**...*..**... + + 15 + stipple27 + + + + ...*..*....*..*. + ....***.....***. + .*..*....*..*... + ..***.....***... + ..*....*..*....* + ***.....***..... + *....*..*....*.. + *.....***.....** + ...*..*....*..*. + ....***.....***. + .*..*....*..*... + ..***.....***... + ..*....*..*....* + ***.....***..... + *....*..*....*.. + *.....***.....** + + 16 + stipple26 + + + + ..**...*..**...* + .**...*..**...*. + **...*..**...*.. + *...*..**...*..* + ...*..**...*..** + ..*..**...*..**. + .*..**...*..**.. + *..**...*..**... + ..**...*..**...* + .**...*..**...*. + **...*..**...*.. + *...*..**...*..* + ...*..**...*..** + ..*..**...*..**. + .*..**...*..**.. + *..**...*..**... + + 17 + stipple25 + + + + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + + 18 + stipple24 + + + + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + + 19 + stipple23 + + + + ....****....**** + ....****....**** + ....****....**** + ....****....**** + ****....****.... + ****....****.... + ****....****.... + ****....****.... + ....****....**** + ....****....**** + ....****....**** + ....****....**** + ****....****.... + ****....****.... + ****....****.... + ****....****.... + + 20 + stipple22 + + + + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + + 21 + stipple21 + + + + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + ..**..**..**..** + ..**..**..**..** + **..**..**..**.. + **..**..**..**.. + + 22 + stipple20 + + + + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + *.*.*.*.*.*.*.*. + ................ + + 23 + stipple19 + + + + ................ + .*.*.*.*.*.*.*.* + ................ + *.*.*.*.*.*.*.*. + ................ + .*.*.*.*.*.*.*.* + ................ + *.*.*.*.*.*.*.*. + ................ + .*.*.*.*.*.*.*.* + ................ + *.*.*.*.*.*.*.*. + ................ + .*.*.*.*.*.*.*.* + ................ + *.*.*.*.*.*.*.*. + + 24 + stipple18 + + + + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + *.*.*.*.*.*.*.*. + + 25 + stipple17 + + + + ................ + **************** + ................ + **************** + ................ + **************** + ................ + **************** + ................ + **************** + ................ + **************** + ................ + **************** + ................ + **************** + + 26 + stipple16 + + + + *.......*....... + *.......*....... + .*.......*...... + .*.......*...... + ..*.......*..... + ..*.......*..... + ...*.......*.... + ...*.......*.... + ....*.......*... + ....*.......*... + .....*.......*.. + .....*.......*.. + ......*.......*. + ......*.......*. + .......*.......* + .......*.......* + + 27 + stipple15 + + + + .......*.......* + .......*.......* + ......*.......*. + ......*.......*. + .....*.......*.. + .....*.......*.. + ....*.......*... + ....*.......*... + ...*.......*.... + ...*.......*.... + ..*.......*..... + ..*.......*..... + .*.......*...... + .*.......*...... + *.......*....... + *.......*....... + + 28 + stipple14 + + + + *............... + .*.............. + ..*............. + ...*............ + ....*........... + .....*.......... + ......*......... + .......*........ + ........*....... + .........*...... + ..........*..... + ...........*.... + ............*... + .............*.. + ..............*. + ...............* + + 29 + stipple13 + + + + *..**..**..**..* + **..**..**..**.. + .**..**..**..**. + ..**..**..**..** + *..**..**..**..* + **..**..**..**.. + .**..**..**..**. + ..**..**..**..** + *..**..**..**..* + **..**..**..**.. + .**..**..**..**. + ..**..**..**..** + *..**..**..**..* + **..**..**..**.. + .**..**..**..**. + ..**..**..**..** + + 30 + stipple12 + + + + .**..**..**..**. + ...*...*...*...* + *...*...*...*... + .**..**..**..**. + .**..**..**..**. + *...*...*...*... + ...*...*...*...* + .**..**..**..**. + .**..**..**..**. + ...*...*...*...* + *...*...*...*... + .**..**..**..**. + .**..**..**..**. + *...*...*...*... + ...*...*...*...* + .**..**..**..**. + + 31 + stipple11 + + + + ..*.*.....*.*... + .*...*...*...*.. + *.....*.*.....*. + .......*.......* + *.....*.*.....*. + .*...*...*...*.. + ..*.*.....*.*... + ...*.......*.... + ..*.*.....*.*... + .*...*...*...*.. + *.....*.*.....*. + .......*.......* + *.....*.*.....*. + .*...*...*...*.. + ..*.*.....*.*... + ...*.......*.... + + 32 + stipple10 + + + + *...........*... + .*...........*.. + ..*...........*. + ...*...........* + ....*........... + .....*.......... + ......*......... + .......*........ + ........*....... + .........*...... + ..........*..... + ...........*.... + *...........*... + .*...........*.. + ..*...........*. + ...*...........* + + 33 + stipple9 + + + + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + ................ + .*.*.*.*.*.*.*.* + + 34 + stipple8 + + + + *...*...*...*... + ................ + ................ + ................ + *...*...*...*... + ................ + ................ + ................ + *...*...*...*... + ................ + ................ + ................ + *...*...*...*... + ................ + ................ + ................ + + 35 + stipple7 + + + + .**..**..**..**. + *...*...*...*... + ...*...*...*...* + .**..**..**..**. + .**..**..**..**. + ...*...*...*...* + *...*...*...*... + .**..**..**..**. + .**..**..**..**. + *...*...*...*... + ...*...*...*...* + .**..**..**..**. + .**..**..**..**. + ...*...*...*...* + *...*...*...*... + .**..**..**..**. + + 36 + stipple6 + + + + ...*...*...*...* + ...*...*...*...* + *...*...*...*... + *...*...*...*... + ...*...*...*...* + ...*...*...*...* + *...*...*...*... + *...*...*...*... + ...*...*...*...* + ...*...*...*...* + *...*...*...*... + *...*...*...*... + ...*...*...*...* + ...*...*...*...* + *...*...*...*... + *...*...*...*... + + 37 + stipple5 + + + + ...**......**... + ..*..*....*..*.. + .*....*..*....*. + *......**......* + *......**......* + .*....*..*....*. + ..*..*....*..*.. + ...**......**... + ...**......**... + ..*..*....*..*.. + .*....*..*....*. + *......**......* + *......**......* + .*....*..*....*. + ..*..*....*..*.. + ...**......**... + + 38 + stipple4 + + + + *...*...*...*... + ................ + ..*...*...*...*. + ................ + *...*...*...*... + ................ + ..*...*...*...*. + ................ + *...*...*...*... + ................ + ..*...*...*...*. + ................ + *...*...*...*... + ................ + ..*...*...*...*. + ................ + + 39 + stipple3 + + + + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + ...*...*...*...* + ..*...*...*...*. + .*...*...*...*.. + *...*...*...*... + + 40 + stipple2 + + + + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + *...*...*...*... + .*...*...*...*.. + ..*...*...*...*. + ...*...*...*...* + + 41 + stipple1 + + + + * + + 42 + stipple0 + + + + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + + 43 + x + + + + ................ + ................ + .......********* + ........*.....*. + .........*...*.. + ..........*.*... + ...........*.... + ................ + ................ + ................ + *********....... + .*.....*........ + ..*...*......... + ...*.*.......... + ....*........... + ................ + + 44 + triangle + + + + .....*.......*.. + .....*.......*.. + .....*.......*.. + ...*****...***** + .....*.......*.. + .....*.......*.. + .....*.......*.. + ................ + ..*.......*..... + ..*.......*..... + ..*.......*..... + *****...*****... + ..*.......*..... + ..*.......*..... + ..*.......*..... + ................ + + 45 + dagger + + + + ......*.......*. + ......*.......*. + ......*.......*. + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + + 46 + brick + + + + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + .....*.......*.. + .....*.......*.. + .....*.......*.. + + 47 + vCurb + + + + ................ + ................ + ****...*****...* + ...*...*...*...* + ...*...*...*...* + ...*****...***** + ................ + ................ + ................ + ................ + ****...*****...* + ...*...*...*...* + ...*...*...*...* + ...*****...***** + ................ + ................ + + 48 + hCurb + + + + ....*.....*....* + ....*....*....*. + ...*....*.....*. + ..*.....*....*.. + ..*....*....*... + .*....*.....*... + *.....*....*.... + *....*....*..... + ....*.....*....* + ....*....*....*. + ...*....*.....*. + ..*.....*....*.. + ..*....*....*... + .*....*.....*... + *.....*....*.... + *....*....*..... + + 49 + vZigZag + + + + .......*.......* + .....**......**. + ....*.......*... + ..**......**.... + .*.......*...... + *......**......* + ......*.......*. + ....**......**.. + ...*.......*.... + .**......**..... + *.......*....... + ......**......** + .....*.......*.. + ...**......**... + ..*.......*..... + **......**...... + + 50 + hZigZag + + + + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + + 51 + grid + + + + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + + 52 + vLine + + + + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + + 53 + hLine + + + + ................ + ................ + ................ + ....*.*.*.*..... + ...*.*.*.*.*.... + ....*........... + ...*............ + ....*........... + ...*...*........ + ....*...*....... + ...*.*.*........ + ....*.*.*....... + ................ + ................ + ................ + ................ + + 54 + spiral + + + + ................ + ......*.*.*.*... + .....*.*.*.*.*.. + ......*......... + .....*.......... + ......*......... + .....*...*...... + ......*...*..... + .....*.*.*...... + ......*.*.*..... + ................ + ................ + ................ + ................ + ................ + ................ + + 55 + spiral22 + + + + *...*........... + .*...*.......*.. + ......*.......*. + ...*...*.......* + *...*........... + .*...*...*...... + ..*...*...*..... + ...*...*...*.... + ........*...*... + .....*...*...*.. + ......*.......*. + .......*...*...* + ........*...*... + .*.......*...*.. + ..*.......*...*. + ...*.......*...* + + 56 + slash3Fill + + + + ................ + .*........*..... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ................ + ...*........*... + ................ + ................ + ................ + ................ + + 57 + dats5 + + + + *.....*.....*... + ................ + ................ + ...*.....*.....* + ................ + ................ + *.....*.....*... + ................ + ................ + ...*.....*.....* + ................ + ................ + *.....*.....*... + ................ + ................ + ...*.....*.....* + + 58 + dots4 + + + + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + + 59 + dots2 + + + + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + + 60 + stipple11 + + + + *.......*....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + *.......*....... + ................ + ................ + ................ + ................ + ................ + ................ + ................ + + 61 + stipple10 + + + + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + .*...*...*...*.. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + ..*...*...*...*. + + 62 + stipple9 + + + + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + ...*...*...*...* + *...*...*...*... + + 63 + stipple8 + + + + ..****....****.. + .*....*..*....*. + *......**......* + *......**......* + *......**......* + *......**......* + .*....*..*....*. + ..****....****.. + ..****....****.. + .*....*..*....*. + *......**......* + *......**......* + *......**......* + *......**......* + .*....*..*....*. + ..****....****.. + + 64 + stipple7 + + + + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + *...*...*...*... + + 65 + stipple3 + + + + ****....****.... + ****....****.... + ****....****.... + ****....****.... + ....****....**** + ....****....**** + ....****....**** + ....****....**** + ****....****.... + ****....****.... + ****....****.... + ****....****.... + ....****....**** + ....****....**** + ....****....**** + ....****....**** + + 66 + stipple2 + + + + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + *...*...*...*... + .*.*.*.*.*.*.*.* + ..*...*...*...*. + .*.*.*.*.*.*.*.* + + 67 + x + + + + ................ + ....*........... + ...*.*.......... + ..*...*......... + .*.....*........ + *********....... + ................ + ................ + ................ + ...........*.... + ..........*.*... + .........*...*.. + ........*.....*. + .......********* + ................ + ................ + + 68 + triangle + + + + ................ + ..*.......*..... + ..*.......*..... + ..*.......*..... + *****...*****... + ..*.......*..... + ..*.......*..... + ..*.......*..... + ................ + .....*.......*.. + .....*.......*.. + .....*.......*.. + ...*****...***** + .....*.......*.. + .....*.......*.. + .....*.......*.. + + 69 + dagger + + + + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + **************** + ..*.......*..... + ..*.......*..... + ..*.......*..... + **************** + ......*.......*. + ......*.......*. + ......*.......*. + + 70 + brick + + + + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + .....*.......*.. + .....*.......*.. + .....*.......*.. + ..****....****.. + ..*.......*..... + ..*.......*..... + ..*.......*..... + ..****....****.. + + 71 + vCurb + + + + ................ + ................ + ...*****...***** + ...*...*...*...* + ...*...*...*...* + ****...*****...* + ................ + ................ + ................ + ................ + ...*****...***** + ...*...*...*...* + ...*...*...*...* + ****...*****...* + ................ + ................ + + 72 + hCurb + + + + *....*....*..... + *.....*....*.... + .*....*.....*... + ..*....*....*... + ..*.....*....*.. + ...*....*.....*. + ....*....*....*. + ....*.....*....* + *....*....*..... + *.....*....*.... + .*....*.....*... + ..*....*....*... + ..*.....*....*.. + ...*....*.....*. + ....*....*....*. + ....*.....*....* + + 73 + vZigZag + + + + **......**...... + ..*.......*..... + ...**......**... + .....*.......*.. + ......**......** + *.......*....... + .**......**..... + ...*.......*.... + ....**......**.. + ......*.......*. + *......**......* + .*.......*...... + ..**......**.... + ....*.......*... + .....**......**. + .......*.......* + + 74 + hZigZag + + + + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + ...*...*...*...* + ...*...*...*...* + ...*...*...*...* + **************** + + 75 + grid + + + + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + ................ + ................ + ................ + **************** + + 76 + hLine + + + + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + ...*...*...*...* + ................ + .*...*...*...*.. + ................ + + 77 + dots1 + + + + * + + 0 + + + + + * + + 0 + + + + + * + + 0 + + + + + * + + 0 + + + diff --git a/technology/freepdk45/tech/scn4m_subm.lyt b/technology/freepdk45/tech/scn4m_subm.lyt new file mode 100644 index 00000000..714dcc92 --- /dev/null +++ b/technology/freepdk45/tech/scn4m_subm.lyt @@ -0,0 +1,205 @@ + + + MOSIS_SCMOS + MOSIS SCMOS lambda based process + + 0.001 + $(appdata_path)/tech/MOSIS_SCMOS + MOSIS_SCMOS.lyp + true + + + 1 + true + true + + + true + layer_map() + true + true + + + true + layer_map() + 0.001 + true + #1 + true + #1 + false + #1 + true + OUTLINE + true + PLACEMENT_BLK + true + REGIONS + true + + 0 + true + .PIN + 2 + true + .PIN + 2 + true + .FILL + 5 + true + .OBS + 3 + true + .BLK + 4 + true + .LABEL + 1 + true + + 0 + true + + 0 + VIA_ + true + default + false + + + + false + true + true + 64 + 0 + 1 + 0 + DATA + 0 + 0 + BORDER + layer_map() + true + + + 0.001 + 1 + 100 + 100 + 0 + 0 + 0 + false + false + false + true + layer_map() + + + 0 + 0.001 + layer_map() + true + false + + + 1 + 0.001 + layer_map() + true + false + true + + + + + + + true + false + false + false + false + false + 8000 + 32000 + LIB + + + 2 + false + false + 1 + * + false + + + 0 + + + false + false + + + 0 + + true + + + + # Provide z stack information here +# Each line is one layer. The specification consists of a layer specification, a colon and arguments. +# The arguments are named (like "x=...") or in serial. Parameters are separated by comma or blanks. +# Named arguments are: +# +# zstart The lower z position of the extruded layer in µm +# zstop The upper z position of the extruded layer in µm +# height The height of the extruded layer in µm +# +# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', # the upper level of the previous layer will be used. +# +# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to +# 'zstart' and 'zstop'. +# +# Examples: +# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically +# 1/0: 0.5 1.5 # same with explicit datatype +# 1: zstop=1.5, zstart=0.5 # same with named parameters +# 1: height=1.0, zstop=1.5 # same with z stop minus height +# 1: 1.0 zstop=1.5 # same with height as unnamed parameter +42: zstart=-0.1 , height=0.1 +41: zstart=-0.1 , height=0.1 +13: zstart=0.001 , height=0.25 +46: zstart=0.1 , height=0.15 +49: zstart=0.2 , height=0.05 +50: zstart=0.2 , height=0.15 +51: zstart=0.3 , height=0.05 +61: zstart=0.3 , height=0.15 +62: zstart=0.4 , height=0.05 +30: zstart=0.4 , height=0.15 +31: zstart=0.5 , height=0.05 +32: zstart=0.5 , height=0.15 +33: zstart=0.6 , height=0.05 +36: zstart=0.6 , height=0.15 +37: zstart=0.7 , height=0.05 +127: zstart=0.7 , height=0.15 +126: zstart=0.8 , height=0.05 +129: zstart=0.9 , height=0.15 +53: zstart=0.8 , height=0.05 +26: zstart=0.9 , height=0.15 +54: zstart=0.9 , height=0.2 + + + + poly,25,49 + 56,55,49 + 49,50,51 + 51,61,62 + 62,30,31 + 31,32,33 + 33,36,37 + poly='46-34' + + diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py index fd477077..76522ff3 100644 --- a/technology/freepdk45/tech/tech.py +++ b/technology/freepdk45/tech/tech.py @@ -6,10 +6,11 @@ # All rights reserved. # import os -from design_rules import * -from module_type import * -from custom_cell_properties import cell_properties -from custom_layer_properties import layer_properties +import drc as d +#from drc.design_rules import design_rules +#from drc.module_type import module_type +#from drc.custom_cell_properties import cell_properties +#from drc.custom_layer_properties import layer_properties """ File containing the process technology parameters for FreePDK 45nm. @@ -24,19 +25,18 @@ File containing the process technology parameters for FreePDK 45nm. # Using tech_modules['cellname'] you can override each class by providing a custom # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules['contact'] = 'contact_freepdk45' -tech_modules = module_type() +tech_modules = d.module_type() ################################################### # Custom cell properties ################################################### -cell_properties = cell_properties() -cell_properties.bitcell_power_pin_directions = ("V", "V") +cell_properties = d.cell_properties() ################################################### # Custom cell properties ################################################### -layer_properties = layer_properties() +layer_properties = d.layer_properties() ################################################### # GDS file info @@ -186,7 +186,7 @@ parameter["6T_access_size"] = 0.135 drclvs_home=os.environ.get("DRCLVS_HOME") -drc = design_rules("freepdk45") +drc = d.design_rules("freepdk45") #grid size drc["grid"] = 0.0025 @@ -337,7 +337,7 @@ drc.add_enclosure("m2", # VIA2-3.2 Minimum spacing of Via[2-3] drc.add_layer("via2", width=0.065, - spacing=0.075) + spacing=0.085) # METALINT.1 Minimum width of intermediate metal # METALINT.2 Minimum spacing of intermediate metal @@ -348,7 +348,7 @@ drc.add_layer("via2", # Minimum spacing of m3 wider than 1.5 & longer than 4.0=1.5 drc.add_layer("m3", width=0.07, - spacing=drc_lut({(0.00, 0.0): 0.07, + spacing=d.drc_lut({(0.00, 0.0): 0.07, (0.09, 0.3): 0.09, (0.27, 0.9): 0.27, (0.50, 1.8): 0.5, @@ -380,7 +380,7 @@ drc.add_layer("via3", # Minimum spacing of m4 wider than 1.5 & longer than 4.0=1.5 drc.add_layer("m4", width=0.14, - spacing=drc_lut({(0.00, 0.0): 0.14, + spacing=d.drc_lut({(0.00, 0.0): 0.14, (0.27, 0.9): 0.27, (0.50, 1.8): 0.5, (0.90, 2.7): 0.9, @@ -457,10 +457,10 @@ parameter["sa_inv_nmos_size"] = 0.27 # micro-meters parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance # Spice Values uses to calculate analytical delay based on CACTI equations -spice["i_on_n"] = 0.0004463 # A/um +spice["i_on_n"] = 0.0004463 # A/um spice["i_on_p"] = 0.0000771 # A/um spice["tox"] = 0.00114 # microns -spice["eps_ox"] = 0.00245e-14 # F/um, calculated from CACTI 45nm data +spice["eps_ox"] = 0.00245e-14 # F/um, calculated from CACTI 45nm data spice["cox"] = spice["eps_ox"]/spice["tox"] # F/um^2 spice["c_g_ideal"] = spice["cox"]*drc["minlength_channel"] # F/um spice["c_overlap"] = 0.2*spice["c_g_ideal"] # F/um @@ -477,8 +477,12 @@ spice["sa_transconductance"] = (spice["mobility_n"])*spice["cox"]*(parameter["sa # Technology Tool Preferences ################################################### -drc_name = "calibre" -lvs_name = "calibre" -pex_name = "calibre" +#drc_name = "calibre" +#lvs_name = "calibre" +#pex_name = "calibre" + +drc_name = "klayout" +lvs_name = "klayout" +pex_name = "klayout" blackbox_bitcell = False diff --git a/technology/scn4m_subm/gds_lib/cell_1rw.gds b/technology/scn4m_subm/gds_lib/cell_1rw.gds index 92523dc1..6f4d7b26 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_1rw.gds and b/technology/scn4m_subm/gds_lib/cell_1rw.gds differ diff --git a/technology/scn4m_subm/gds_lib/cell_2rw.gds b/technology/scn4m_subm/gds_lib/cell_2rw.gds index a45c34e9..cc0c0873 100644 Binary files a/technology/scn4m_subm/gds_lib/cell_2rw.gds and b/technology/scn4m_subm/gds_lib/cell_2rw.gds differ diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds b/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds index b27e5e02..3f678558 100644 Binary files a/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds and b/technology/scn4m_subm/gds_lib/dummy_cell_1rw.gds differ diff --git a/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds b/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds index 4f0427fd..f8fd6760 100644 Binary files a/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds and b/technology/scn4m_subm/gds_lib/dummy_cell_2rw.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds b/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds index 7eee1179..a5b5e3d2 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds and b/technology/scn4m_subm/gds_lib/replica_cell_1rw.gds differ diff --git a/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds b/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds index c520422a..df0f9e28 100644 Binary files a/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds and b/technology/scn4m_subm/gds_lib/replica_cell_2rw.gds differ diff --git a/technology/scn4m_subm/gds_lib/write_driver.gds b/technology/scn4m_subm/gds_lib/write_driver.gds index 9e201f24..c9431117 100644 Binary files a/technology/scn4m_subm/gds_lib/write_driver.gds and b/technology/scn4m_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn4m_subm/tech/.magicrc b/technology/scn4m_subm/tech/.magicrc index c85bb879..8fdad1b7 100644 --- a/technology/scn4m_subm/tech/.magicrc +++ b/technology/scn4m_subm/tech/.magicrc @@ -6,3 +6,4 @@ tech load SCN4M_SUBM.20 -noprompt scalegrid 1 4 set GND gnd set VDD vdd +set SUB gnd diff --git a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech index 7207f681..3d83bb96 100644 --- a/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech +++ b/technology/scn4m_subm/tech/SCN4M_SUBM.20.tech @@ -333,7 +333,7 @@ style lambda=0.20(p) templayer TCSB nwsd,nwsc grow 40 and-not TNWR - + layer CSB nwsd,nwsc,nwr grow 140 and-not TCSB @@ -375,7 +375,7 @@ style lambda=0.20(p) bloat-or pdiff,apres,rpd,pdc/a * 40 nsd,nsc/a 0 or TPS -#final pselect +#final pselect templayer FSP bloat-or pdiff,apres,rpd,pfet,psd,pdc/a,psc/a,pfet * 40 ndiff,anres,rnd,ndc/a,nsd,nsc/a,nfet 0 or XDP @@ -772,7 +772,7 @@ style lambda=0.20(cp) templayer TCSB nwsd,nwsc grow 40 and-not TNWR - + layer CSB nwsd,nwsc,nwr grow 140 and-not TCSB @@ -814,7 +814,7 @@ style lambda=0.20(cp) bloat-or pdiff,apres,rpd,pdc/a * 40 nsd,nsc/a 0 or TPS -#final pselect +#final pselect templayer FSP bloat-or pdiff,apres,rpd,pfet,psd,pdc/a,psc/a,pfet * 40 ndiff,anres,rnd,ndc/a,nsd,nsc/a,nfet 0 or XDP @@ -1119,7 +1119,7 @@ style lambda=0.20(c) templayer TCSB nwsd,nwsc grow 40 and-not TNWR - + layer CSB nwsd,nwsc,nwr grow 140 and-not TCSB @@ -1151,7 +1151,7 @@ style lambda=0.20(c) bloat-or pdiff,apres,rpd,pdc/a * 40 nsd,nsc/a 0 or TPS -#final pselect +#final pselect templayer FSP bloat-or pdiff,apres,rpd,pfet,psd,pdc/a,psc/a,pfet * 40 ndiff,anres,rnd,ndc/a,nsd,nsc/a,nfet 0 or XDP @@ -1456,7 +1456,7 @@ style lambda=0.20() templayer TCSB nwsd,nwsc grow 40 and-not TNWR - + layer CSB nwsd,nwsc,nwr grow 140 and-not TCSB @@ -1488,7 +1488,7 @@ style lambda=0.20() bloat-or pdiff,apres,rpd,pdc/a * 40 nsd,nsc/a 0 or TPS -#final pselect +#final pselect templayer FSP bloat-or pdiff,apres,rpd,pfet,psd,pdc/a,psc/a,pfet * 40 ndiff,anres,rnd,ndc/a,nsd,nsc/a,nfet 0 or XDP @@ -6471,7 +6471,7 @@ end mzrouter style irouter -# layer hCost vCost jogCost hintCost +# layer hCost vCost jogCost hintCost layer metal4 2 1 2 1 layer metal3 1 2 2 1 layer metal2 2 1 2 1 @@ -6944,7 +6944,7 @@ drc "Silicide-Block overlap of Silicide-Block polyR/activeR < 2 (Mosis #20.15)" edge4way sb,pres,anres,apres diff,ndiff,rnd,nfet,nsd,nwsd,pdiff,rpd,pfet,psd,ndc/a,nsc/a,nwsc/a,pdc/a,psc/a 3 diff,ndiff,rnd,nfet,nsd,nwsd,pdiff,rpd,pfet,psd,ndc/a,nsc/a,nwsc/a,pdc/a,psc/a 0 0 \ - "Diffusion overhang of Silicide-Block < 3 (Mosis #20.17) + "Diffusion overhang of Silicide-Block < 3 (Mosis #20.17) spacing gv3 gv3 3 touching_ok \ "GV3 via spacing < 3 (Mosis #21.2)" @@ -7145,7 +7145,7 @@ drc spacing m4p m4p 4 touching_ok \ "Metal4 PIN spacing < 4 (do_pins)" -#CC cifstyle lambda=0.20(p) +#CC cifstyle lambda=0.20(p) #CC cifwidth CWN 240 \ #CC "generated CIF layer CWN width will be < 12 (';cif see CWN')" #CC cifspacing CWN CWN 120 touching_ok \ @@ -7223,6 +7223,7 @@ extract resist (m2,fm2,rm2,m2c,m3c,m3c)/metal2 70 resist (m3,fm3,rm3,m3c,m4c,m4c)/metal3 80 resist (m4,fm4,rm4,m4c,pad)/metal4 40 + resist bb None contact ndc 4 4100 contact pdc 4 3400 @@ -7288,25 +7289,25 @@ extract sideoverlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~space/w 2.226 ~space/a #rnw - overlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 rnw,nwr/active 1.666 - sideoverlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 rnw,nwr/active 2.226 + overlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 rnw,nwr/active 1.666 + sideoverlap (m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,pdc,pc,m2c)/metal1 rnw,nwr/active 2.226 -#metal1-diff blocked by - overlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (ndiff,anres,rnd,ndc)/active 1.640 - sideoverlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (ndiff,anres,rnd,ndc)/active 2.226 - overlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (pdiff,apres,rpd,pdc)/active 1.640 - sideoverlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (pdiff,apres,rpd,pdc)/active 2.226 +#metal1-diff blocked by + overlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (ndiff,anres,rnd,ndc)/active 1.640 + sideoverlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (ndiff,anres,rnd,ndc)/active 2.226 + overlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (pdiff,apres,rpd,pdc)/active 1.640 + sideoverlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (pdiff,apres,rpd,pdc)/active 2.226 -#metal1-poly blocked by - overlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (poly,fp,pres,rp,pc,nfet,pfet,fet)/active 1.687 - sideoverlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (poly,fp,pres,rp,pc,nfet,pfet,fet)/active 2.250 - sideoverlap (poly,fp,pres,rp,pc,nfet,pfet,fet)/active ~(poly,fp,pres,rp,pc,nfet,pfet,fet)/active (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 2.250 +#metal1-poly blocked by + overlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (poly,fp,pres,rp,pc,nfet,pfet,fet)/active 1.687 + sideoverlap (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 ~(m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 (poly,fp,pres,rp,pc,nfet,pfet,fet)/active 2.250 + sideoverlap (poly,fp,pres,rp,pc,nfet,pfet,fet)/active ~(poly,fp,pres,rp,pc,nfet,pfet,fet)/active (m1,fm1,rm1,ndc,nsc,nwsc,pdc,psc,pc,m2c)/metal1 2.250 #metal2 sidewall (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 (m2,fm2,rm2,m2c,m3c)/metal2 23.532 areacap (m2,fm2,rm2,m3c)/metal2 0.581 -#metal2-sub blocked by +#metal2-sub blocked by overlap (m2,fm2,rm2,m3c)/metal2 ~space/w 0.581 ~space/a,~space/m1 perimc (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 0.836 sideoverlap (m2,fm2,rm2,m2c,m3c)/metal2 ~(m2,fm2,rm2,m2c,m3c)/metal2 ~space/w 0.836 ~space/a,~space/m1 @@ -7732,4 +7733,3 @@ style gremlin pc/a,ndc/a,pdc/a,psc/a,nsc/a,nwsc/a,gc,gc,gc,gv1 X end - diff --git a/technology/scn4m_subm/tech/scn4m_subm.lydrc b/technology/scn4m_subm/tech/scn4m_subm.lydrc new file mode 100644 index 00000000..68804569 --- /dev/null +++ b/technology/scn4m_subm/tech/scn4m_subm.lydrc @@ -0,0 +1,810 @@ + + + + + drc + + + + false + false + + true + drc_scripts + tools_menu.drc.end + dsl + drc-dsl-xml + # +# MOSIS SCMOS DRC +# +######################## +tstart = Time.now + +# optionnal for a batch launch : klayout -b r drc_SCMOS.lydrc -rd input=my_layout.gds -rd topcell=your_topcell -rd output=SCMOS_DRC.lyrdb +if $input + if $topcell + source($input,$topcell) + else + source($input) + end +end + +if $output + report("SCMOS DRC runset", $output) +else + report("SCMOS DRC runset", "SCMOS_DRC.lyrdb") +end + + +# PROCESS OPTIONS +######################## +LAMBDA = 0.2 +SUBM = true +DEEP = false +NBR_OF_METALS = 6 + +DFM = false + +# design rules limits definitions +######################## +R1_3 = ( 6 *LAMBDA ).round(3) +R2_1 = ( 3 *LAMBDA ).round(3) +R2_2 = ( 3 *LAMBDA ).round(3) +R2_4 = ( 3 *LAMBDA ).round(3) +R2_5 = ( 4 *LAMBDA ).round(3) +R3_1 = ( 2 *LAMBDA ).round(3) +R3_5 = ( 1 *LAMBDA ).round(3) +R4_1 = ( 3 *LAMBDA ).round(3) +R4_2 = ( 2 *LAMBDA ).round(3) +R5_1 = ( 2 *LAMBDA ).round(3) +R5_2 = ( 1.5 *LAMBDA ).round(3) +R5_4 = ( 2 *LAMBDA ).round(3) +R5_2_b = ( 1 *LAMBDA ).round(3) +R5_6_b = ( 2 *LAMBDA ).round(3) +R5_7_b = ( 3 *LAMBDA ).round(3) +R6_1 = ( 2 *LAMBDA ).round(3) +R6_2 = ( 1.5 *LAMBDA ).round(3) +R6_4 = ( 2 *LAMBDA ).round(3) +R6_2_b = ( 1 *LAMBDA ).round(3) +R6_5_b = ( 5 *LAMBDA ).round(3) +R6_6_b = ( 2 *LAMBDA ).round(3) +R6_7_b = ( 3 *LAMBDA ).round(3) +R6_8_b = ( 4 *LAMBDA ).round(3) +R7_1 = ( 3 *LAMBDA ).round(3) +R7_3 = ( 1 *LAMBDA ).round(3) +R8_2 = ( 3 *LAMBDA ).round(3) +R8_3 = ( 1 *LAMBDA ).round(3) +R8_4 = ( 2 *LAMBDA ).round(3) +R9_1 = ( 3 *LAMBDA ).round(3) +R9_3 = ( 1 *LAMBDA ).round(3) +R10_1 = ( 60 *LAMBDA ).round(3) +R10_2 = ( 20 *LAMBDA ).round(3) +R10_3 = ( 6 *LAMBDA ).round(3) +R10_4 = ( 30 *LAMBDA ).round(3) +R10_5 = ( 15 *LAMBDA ).round(3) +R11_2 = ( 3 *LAMBDA ).round(3) +R11_4 = ( 2 *LAMBDA ).round(3) +R11_6 = ( 2 *LAMBDA ).round(3) +R12_1 = ( 2 *LAMBDA ).round(3) +R12_2 = ( 3 *LAMBDA ).round(3) +R12_3 = ( 2 *LAMBDA ).round(3) +R12_4 = ( 1 *LAMBDA ).round(3) +R12_5 = ( 2 *LAMBDA ).round(3) +R12_6 = ( 3 *LAMBDA ).round(3) +R13_1 = ( 2 *LAMBDA ).round(3) +R13_3 = ( 3 *LAMBDA ).round(3) +R13_4 = ( 2 *LAMBDA ).round(3) +R13_5 = ( 3 *LAMBDA ).round(3) +R14_2 = ( 3 *LAMBDA ).round(3) +R14_3 = ( 1 *LAMBDA ).round(3) +R14_4 = ( 2 *LAMBDA ).round(3) +if NBR_OF_METALS > 3 + R15_3 = ( 1 *LAMBDA ).round(3) +else + R15_3 = ( 2 *LAMBDA ).round(3) +end +R16_1 = ( 2 *LAMBDA ).round(3) +R16_2 = ( 3 *LAMBDA ).round(3) +R16_3 = ( 2 *LAMBDA ).round(3) +R16_4 = ( 4 *LAMBDA ).round(3) +R16_5 = ( 2 *LAMBDA ).round(3) +R16_6 = ( 2 *LAMBDA ).round(3) +R16_7 = ( 6 *LAMBDA ).round(3) +R16_8 = ( 4 *LAMBDA ).round(3) +R16_9 = ( 2 *LAMBDA ).round(3) +R16_10 = ( 3 *LAMBDA ).round(3) +R16_11 = ( 2 *LAMBDA ).round(3) +R18_1 = ( 3 *LAMBDA ).round(3) +R18_2 = ( 2 *LAMBDA ).round(3) +R18_3 = ( 3 *LAMBDA ).round(3) +R18_4 = ( 2 *LAMBDA ).round(3) +R18_5 = ( 6 *LAMBDA ).round(3) +R20_1 = ( 4 *LAMBDA ).round(3) +R20_2 = ( 4 *LAMBDA ).round(3) +R20_3 = ( 2 *LAMBDA ).round(3) +R20_4 = ( 2 *LAMBDA ).round(3) +R20_5 = ( 2 *LAMBDA ).round(3) +R20_7 = ( 5 *LAMBDA ).round(3) +R20_8 = ( 7 *LAMBDA ).round(3) +R20_9 = ( 2 *LAMBDA ).round(3) +R20_10 = ( 3 *LAMBDA ).round(3) +R21_2 = ( 3 *LAMBDA ).round(3) +R21_3 = ( 1 *LAMBDA ).round(3) +if NBR_OF_METALS > 4 + R22_3 = ( 1 *LAMBDA ).round(3) +else + R22_3 = ( 2 *LAMBDA ).round(3) +end +R23_1 = ( 8 *LAMBDA ).round(3) +R23_2 = ( 4 *LAMBDA ).round(3) +R23_3 = ( 8 *LAMBDA ).round(3) +R23_4 = ( 3 *LAMBDA ).round(3) +R23_5 = ( 2 *LAMBDA ).round(3) +R23_6 = ( 2 *LAMBDA ).round(3) +R23_7 = ( 2 *LAMBDA ).round(3) +R23_8 = ( 4 *LAMBDA ).round(3) +R23_9 = ( 2 *LAMBDA ).round(3) +R24_1 = ( 4 *LAMBDA ).round(3) +R24_2 = ( 4 *LAMBDA ).round(3) +R24_3 = ( 4 *LAMBDA ).round(3) +R24_4 = ( 4 *LAMBDA ).round(3) +R24_5 = ( 3 *LAMBDA ).round(3) +R27_1 = ( 4 *LAMBDA ).round(3) +R27_2 = ( 4 *LAMBDA ).round(3) +R27_3 = ( 2 *LAMBDA ).round(3) +R27_4 = ( 2 *LAMBDA ).round(3) +R27_5 = ( 2 *LAMBDA ).round(3) +R27_7 = ( 5 *LAMBDA ).round(3) +R27_8 = ( 7 *LAMBDA ).round(3) +R27_9 = ( 2 *LAMBDA ).round(3) + + +if !SUBM && !DEEP + R1_1 = ( 10 *LAMBDA ).round(3) + R1_2 = ( 9 *LAMBDA ).round(3) + R2_3 = ( 5 *LAMBDA ).round(3) + R3_2 = ( 2 *LAMBDA ).round(3) + R3_2_a = ( 2 *LAMBDA ).round(3) + R3_3 = ( 2 *LAMBDA ).round(3) + R3_4 = ( 3 *LAMBDA ).round(3) + R4_3 = ( 1 *LAMBDA ).round(3) + R4_4 = ( 2 *LAMBDA ).round(3) + R5_3 = ( 2 *LAMBDA ).round(3) + R5_5_b = ( 4 *LAMBDA ).round(3) + R6_3 = ( 2 *LAMBDA ).round(3) + R7_2 = ( 2 *LAMBDA ).round(3) + R7_4 = ( 4 *LAMBDA ).round(3) + R8_1 = ( 2 *LAMBDA ).round(3) + R8_5 = ( 2 *LAMBDA ).round(3) + R9_2 = ( 3 *LAMBDA ).round(3) + R9_4 = ( 6 *LAMBDA ).round(3) + R11_1 = ( 3 *LAMBDA ).round(3) + R11_3 = ( 2 *LAMBDA ).round(3) + R11_5 = ( 3 *LAMBDA ).round(3) + R13_2 = ( 2 *LAMBDA ).round(3) + R14_1 = ( 2 *LAMBDA ).round(3) + if NBR_OF_METALS > 3 + R15_1 = ( 3 *LAMBDA ).round(3) + R15_2 = ( 3 *LAMBDA ).round(3) + R15_4 = ( 6 *LAMBDA ).round(3) + else + R15_1 = ( 6 *LAMBDA ).round(3) + R15_2 = ( 4 *LAMBDA ).round(3) + R15_4 = ( 8 *LAMBDA ).round(3) + end + R17_1 = ( 10 *LAMBDA ).round(3) + R17_2 = ( 9 *LAMBDA ).round(3) + R17_3 = ( 5 *LAMBDA ).round(3) + R17_4 = ( 5 *LAMBDA ).round(3) + R20_11 = ( 3 *LAMBDA ).round(3) + R21_1 = ( 2 *LAMBDA ).round(3) + if NBR_OF_METALS > 4 + R22_1 = ( 3 *LAMBDA ).round(3) + R22_2 = ( 3 *LAMBDA ).round(3) + R22_4 = ( 6 *LAMBDA ).round(3) + else + R22_1 = ( 6 *LAMBDA ).round(3) + R22_2 = ( 6 *LAMBDA ).round(3) + R22_4 = ( 12 *LAMBDA ).round(3) + end +end + +if SUBM + R1_1 = ( 12 *LAMBDA ).round(3) + # We are assuming the wells are at the same potential since + # DRC can't tell otherwise + #R1_2 = ( 18 *LAMBDA ).round(3) + R1_2 = ( 6 *LAMBDA ).round(3) + R2_3 = ( 6 *LAMBDA ).round(3) + R3_2 = ( 3 *LAMBDA ).round(3) + R3_2_a = ( 3 *LAMBDA ).round(3) + R3_3 = ( 2 *LAMBDA ).round(3) + R3_4 = ( 3 *LAMBDA ).round(3) + R4_3 = ( 1 *LAMBDA ).round(3) + R4_4 = ( 2 *LAMBDA ).round(3) + R5_3 = ( 3 *LAMBDA ).round(3) + R5_5_b = ( 5 *LAMBDA ).round(3) + R6_3 = ( 3 *LAMBDA ).round(3) + R7_2 = ( 3 *LAMBDA ).round(3) + R7_4 = ( 6 *LAMBDA ).round(3) + R8_1 = ( 2 *LAMBDA ).round(3) + R8_5 = ( 2 *LAMBDA ).round(3) + R9_2 = ( 3 *LAMBDA ).round(3) + R9_4 = ( 6 *LAMBDA ).round(3) + R11_1 = ( 7 *LAMBDA ).round(3) + R11_3 = ( 5 *LAMBDA ).round(3) + R11_5 = ( 6 *LAMBDA ).round(3) + R13_2 = ( 3 *LAMBDA ).round(3) + R14_1 = ( 2 *LAMBDA ).round(3) + if NBR_OF_METALS > 3 + R15_1 = ( 3 *LAMBDA ).round(3) + R15_2 = ( 3 *LAMBDA ).round(3) + R15_4 = ( 6 *LAMBDA ).round(3) + else + R15_1 = ( 5 *LAMBDA ).round(3) + R15_2 = ( 3 *LAMBDA ).round(3) + R15_4 = ( 6 *LAMBDA ).round(3) + end + R17_1 = ( 12 *LAMBDA ).round(3) + R17_2 = ( 18 *LAMBDA ).round(3) + R17_3 = ( 6 *LAMBDA ).round(3) + R17_4 = ( 6 *LAMBDA ).round(3) + R20_11 = ( 5 *LAMBDA ).round(3) + R21_1 = ( 2 *LAMBDA ).round(3) + if NBR_OF_METALS > 4 + R22_1 = ( 3 *LAMBDA ).round(3) + R22_2 = ( 3 *LAMBDA ).round(3) + R22_4 = ( 6 *LAMBDA ).round(3) + else + R22_1 = ( 6 *LAMBDA ).round(3) + R22_2 = ( 6 *LAMBDA ).round(3) + R22_4 = ( 12 *LAMBDA ).round(3) + end + R25_1 = ( 2 *LAMBDA ).round(3) + R25_2 = ( 3 *LAMBDA ).round(3) + R25_3 = ( 1 *LAMBDA ).round(3) + if NBR_OF_METALS > 5 + R26_1 = ( 3 *LAMBDA ).round(3) + R26_2 = ( 3 *LAMBDA ).round(3) + R26_3 = ( 1 *LAMBDA ).round(3) + R26_4 = ( 6 *LAMBDA ).round(3) + else + R26_1 = ( 4 *LAMBDA ).round(3) + R26_2 = ( 4 *LAMBDA ).round(3) + R26_3 = ( 2 *LAMBDA ).round(3) + R26_4 = ( 8 *LAMBDA ).round(3) + end + R28_1 = ( 40 *LAMBDA ).round(3) + R28_2 = ( 12 *LAMBDA ).round(3) + R28_3 = ( 4 *LAMBDA ).round(3) + R28_4 = ( 3 *LAMBDA ).round(3) + R28_5 = ( 4 *LAMBDA ).round(3) + R28_6 = ( 2 *LAMBDA ).round(3) + R28_7 = ( 25 *LAMBDA ).round(3) + R28_8 = ( 4 *LAMBDA ).round(3) + R28_9 = ( 8 *LAMBDA ).round(3) + R28_10 = ( 20 *LAMBDA ).round(3) + R28_11 = ( 40 *LAMBDA ).round(3) + R29_1 = ( 3 *LAMBDA ).round(3) + R29_2 = ( 4 *LAMBDA ).round(3) + R29_3 = ( 1 *LAMBDA ).round(3) + R30_1 = ( 5 *LAMBDA ).round(3) + R30_2 = ( 5 *LAMBDA ).round(3) + R30_3 = ( 1 *LAMBDA ).round(3) + R30_4 = ( 10 *LAMBDA ).round(3) + R31_1 = ( 30 *LAMBDA ).round(3) + R31_2 = ( 50 *LAMBDA ).round(3) + R31_3 = ( 15 *LAMBDA ).round(3) + R31_4 = ( 20 *LAMBDA ).round(3) + R31_5 = ( 35 *LAMBDA ).round(3) + R31_6 = ( 5 *LAMBDA ).round(3) + R31_7 = ( 30 *LAMBDA ).round(3) + R31_8 = ( 10 *LAMBDA ).round(3) +end + +if DEEP + R1_1 = ( 12 *LAMBDA ).round(3) + # We are assuming the wells are at the same potential since + # DRC can't tell otherwise + #R1_2 = ( 18 *LAMBDA ).round(3) + R1_2 = ( 6 *LAMBDA ).round(3) + R2_3 = ( 6 *LAMBDA ).round(3) + R3_2 = ( 3 *LAMBDA ).round(3) + R3_2_a = ( 4 *LAMBDA ).round(3) + R3_3 = ( 2.5 *LAMBDA ).round(3) + R3_4 = ( 4 *LAMBDA ).round(3) + R4_3 = ( 1.5 *LAMBDA ).round(3) + R4_4 = ( 4 *LAMBDA ).round(3) + R5_3 = ( 4 *LAMBDA ).round(3) + R5_5_b = ( 5 *LAMBDA ).round(3) + R6_3 = ( 4 *LAMBDA ).round(3) + R7_2 = ( 3 *LAMBDA ).round(3) + R7_4 = ( 6 *LAMBDA ).round(3) + R8_1 = ( 3 *LAMBDA ).round(3) + R9_2 = ( 4 *LAMBDA ).round(3) + R9_4 = ( 8 *LAMBDA ).round(3) + R11_1 = ( 7 *LAMBDA ).round(3) + R11_3 = ( 5 *LAMBDA ).round(3) + R11_5 = ( 6 *LAMBDA ).round(3) + R13_2 = ( 3 *LAMBDA ).round(3) + R14_1 = ( 3 *LAMBDA ).round(3) + if NBR_OF_METALS > 3 + R15_1 = ( 3 *LAMBDA ).round(3) + R15_2 = ( 4 *LAMBDA ).round(3) + R15_4 = ( 8 *LAMBDA ).round(3) + else + R15_1 = ( 5 *LAMBDA ).round(3) + R15_2 = ( 3 *LAMBDA ).round(3) + R15_4 = ( 6 *LAMBDA ).round(3) + end + R17_1 = ( 12 *LAMBDA ).round(3) + R17_2 = ( 18 *LAMBDA ).round(3) + R17_3 = ( 6 *LAMBDA ).round(3) + R17_4 = ( 6 *LAMBDA ).round(3) + R20_11 = ( 5 *LAMBDA ).round(3) + R21_1 = ( 3 *LAMBDA ).round(3) + if NBR_OF_METALS > 4 + R22_1 = ( 3 *LAMBDA ).round(3) + R22_2 = ( 4 *LAMBDA ).round(3) + R22_4 = ( 8 *LAMBDA ).round(3) + else + R22_1 = ( 6 *LAMBDA ).round(3) + R22_2 = ( 6 *LAMBDA ).round(3) + R22_4 = ( 12 *LAMBDA ).round(3) + end + R25_1 = ( 3 *LAMBDA ).round(3) + R25_2 = ( 3 *LAMBDA ).round(3) + R25_3 = ( 1 *LAMBDA ).round(3) + if NBR_OF_METALS > 5 + R26_1 = ( 3 *LAMBDA ).round(3) + R26_2 = ( 4 *LAMBDA ).round(3) + R26_3 = ( 1 *LAMBDA ).round(3) + R26_4 = ( 8 *LAMBDA ).round(3) + else + R26_1 = ( 4 *LAMBDA ).round(3) + R26_2 = ( 4 *LAMBDA ).round(3) + R26_3 = ( 2 *LAMBDA ).round(3) + R26_4 = ( 8 *LAMBDA ).round(3) + end + R28_1 = ( 45 *LAMBDA ).round(3) + R28_2 = ( 14 *LAMBDA ).round(3) + R28_3 = ( 5 *LAMBDA ).round(3) + R28_4 = ( 3 *LAMBDA ).round(3) + R28_5 = ( 5 *LAMBDA ).round(3) + R28_6 = ( 2 *LAMBDA ).round(3) + R28_7 = ( 25 *LAMBDA ).round(3) + R28_8 = ( 5 *LAMBDA ).round(3) + R28_9 = ( 9 *LAMBDA ).round(3) + R28_10 = ( 23 *LAMBDA ).round(3) + R28_11 = ( 45 *LAMBDA ).round(3) + R29_1 = ( 4 *LAMBDA ).round(3) + R29_2 = ( 4 *LAMBDA ).round(3) + R29_3 = ( 1 *LAMBDA ).round(3) + R30_1 = ( 5 *LAMBDA ).round(3) + R30_2 = ( 5 *LAMBDA ).round(3) + R30_3 = ( 2 *LAMBDA ).round(3) + R30_4 = ( 10 *LAMBDA ).round(3) + R31_1 = ( 34 *LAMBDA ).round(3) + R31_2 = ( 56 *LAMBDA ).round(3) + R31_3 = ( 17 *LAMBDA ).round(3) + R31_4 = ( 23 *LAMBDA ).round(3) + R31_5 = ( 39 *LAMBDA ).round(3) + R31_6 = ( 6 *LAMBDA ).round(3) + R31_7 = ( 34 *LAMBDA ).round(3) + R31_8 = ( 13 *LAMBDA ).round(3) +end + + +# KLAYOUT setttings +######################## +# Use a tile size of 1mm +tiles(1000.um) +# Use a tile border of 10 micron: +tile_borders(1.um) +#no_borders + +# Use 4 CPU cores +threads(4) +verbose(true) + +# Define a new custom function that selects polygons by their number of holes: +# It will return a new layer containing those polygons with min to max holes. +# max can be nil to omit the upper limit. +class DRC::DRCLayer + def with_holes(min, max) + new_data = RBA::Region::new + self.data.each do |p| + if p.holes >= (min || 0) && (!max || p.holes <= max) + new_data.insert(p) + end + end + DRC::DRCLayer::new(@engine, new_data) + end +end + +# layers definitions +######################## +info("Layers definitions") + + DNW = input(38,0) + NW = input(42,0) + PW = input(41,0) + CW = input(59,0) + AA = input(43,0) + TA = input(60,0) + PBase = input(58,0) + PL = input(46,0) + SB = input(29,0) + Nselect = input(45,0) + Pselect = input(44,0) + PO2 = input(56,0) + HR = input(34,0) + Contact = input(25,0) + ContactPoly = input(47,0) + ContactActive = input(48,0) + ContactPoly2 = input(55, 0) + CT = Contact + ContactPoly + ContactActive + ContactPoly2 + M1 = input(49,0) + V1 = input(50,0) + M2 = input(51,0) + V2 = input(61,0) + M3 = input(62,0) + V3 = input(30,0) + M4 = input(31,0) + CTM = input(35,0) + V4 = input(32,0) + M5 = input(33,0) + V5 = input(36,0) + M6 = input(37,0) + Glass = input(52,0) + Pads = input(26,0) + +# layers processing +######################## +info("Layers processing") +#CHIP = extent.sized(1.0) +NP = AA & Nselect +PP = AA & Pselect +NSTP = NP.and(NW) +PSTP = PP.not(NW) +GATE = PL & AA + +# DRC section +######################## +info("DRC section") + +### Deep NWell +DNW.ongrid(0.5*LAMBDA).output("DNW_offgrid", "Offgrid vertex on DNW") +DNW.with_angle(0 .. 45).output("DNW_angle", "Non 45 degree angle DNW") +DNW.edges.and(PW).output("DNW_PW","DNW cannot cross PWell") +DNW.width(R31_1, euclidian).output("31.1 DNW_width", "31.1 : Min. DNW width : #{R31_1}um") +DNW.isolated(R31_2, euclidian).output("31.2 DNW_space", "31.2 : Min. DNW spacing : #{R31_2}um") +NW.enclosing(DNW, R31_3, euclidian).output("31.3 NW_enc_DNW", "31.3 : Min. NWell enclosing DNW : #{R31_3}um") +DNW.enclosing(NW, R31_4, euclidian).output("31.4 DNW_enc_NW", "31.4 : Min. DNW enclosing NWell : #{R31_4}um") +DNW.separation(NW, R31_5, euclidian).output("31.5 DNW_sep_NW", "31.5 : Min. DNW separation NWell : #{R31_5}um") +DNW.not(NW).enclosing(NP, R31_6, euclidian).output("31.6 DNW_enc_NP", "31.6 : Min. PW in DNW enclosing N+ : #{R31_6}um") +DNW.separation(NP, R31_7, euclidian).output("31.7 DNW_sep_NP", "31.7 : Min. DNW separation N+ : #{R31_7}um") + +### Nwell / Pwell +NW.ongrid(0.5*LAMBDA).output("NW_offgrid", "Offgrid vertex on NWell") +NW.with_angle(0 .. 45).output("NW_angle", "Non 45 degree angle NWell") +NW.and(PW).output("NW_PW","NW over PW not allowed") +NW.width(R1_1, euclidian).output("1.1 NW_width", "1.1 : Min. NWell width : #{R1_1}um") +NW.isolated(R1_2, euclidian).output("1.2 NW_space", "1.2 : Min. NWell spacing : #{R1_2}um") +NW.and(TA).isolated(18, euclidian).output("1.3 NW_TA_space", "1.3 : Min. NWell-TA spacing : #{R1_3}um") +PW.width(R1_1, euclidian).output("1.1 PW_width", "1.1 : Min. PWell width : #{R1_1}um") +PW.isolated(R1_2, euclidian).output("1.2 PW_space", "1.2 : Min. PWell spacing : #{R1_2}um") +PW.and(TA).isolated(R1_3, euclidian).output("1.3 PW_TA_space", "1.3 : Min. PWell-TA spacing : #{R1_3}um") + +### Active +AA.ongrid(0.5*LAMBDA).output("AA_offgrid", "Offgrid vertex on AA") +AA.with_angle(0 .. 45).output("AA_angle", "Non 45 degree angle AA") +AA.width(R2_1, euclidian).output("2.1 AA_width", "2.1 : Min. active width : #{R2_1}um") +AA.space(R2_2, euclidian).output("2.2 AA_space", "2.2 : Min. active spacing : #{R2_2}3um") +NW.enclosing(PP.interacting(GATE), R2_3, euclidian).output("2.3 NW_enc_PP", "2.3 : Min. NWell enclosing Source/Drain : #{R2_3}um") +PW.enclosing(NP.interacting(GATE), R2_3, euclidian).output("2.3 PW_enc_NP", "2.3 : Min. PWell enclosing Source/Drain : #{R2_3}um") +NW.enclosing(NP, R2_4, euclidian).output("2.4 NW_enc_NP", "2.4 : Min. NWell enclosing Nstrap : #{R2_4}um") +PW.enclosing(PP, R2_4, euclidian).output("2.4 PW_enc_PP", "2.4 : Min. PWell enclosing Pstrap : #{R2_4}um") +NP.separation(PP, R2_5, euclidian).polygons.without_area(0).output("2.5 NP_space_PP", "2.5 : Min. N+ space P+ : #{R2_5}um (if not abutted)") + +### TA Thick Active +TA.ongrid(0.5*LAMBDA).output("TA_offgrid", "Offgrid vertex on TA") +TA.with_angle(0 .. 45).output("TA_angle", "Non 45 degree angle TA") +TA.width(R24_1, euclidian).output("24.1 TA_width", "24.1 : Min. TA width : #{R24_1}um") +TA.space(R24_2, euclidian).output("24.2 TA_space", "24.2 : Min. TA spacing : #{R24_2}um") +TA.enclosing(AA, R24_3, euclidian).output("24.3 TA_enc_AA", "24.3 : Min. TA enclosing Active : #{R24_3}um") +TA.separation(AA, R24_4, euclidian).output("24.4 TA_space_AA", "24.4 : Min. TA spacing Active : #{R24_4}um") +TA.and(GATE).width(R24_5, euclidian).output("24.5 TA_gate_width", "24.5 : Min. TA Gate width : #{R24_5}um") +AA.edges.and(TA).output("24.6 AA_in_TA","24.6 : Active edge cannot cross TA") + +### Poly +PL.ongrid(0.5*LAMBDA).output("POLY_offgrid", "Offgrid vertex on Poly") +PL.with_angle(0 .. 45).output("POLY_angle", "Non 45 degree angle Poly") +PL.width(R3_1, euclidian).output("3.1 POLY_width", "3.1 : Min. Poly width : #{R3_1}um") +PL.space(R3_2, euclidian).output("3.2 Poly_space", "3.2 : Min. Poly spacing : #{R3_2}um") +GATE.space(R3_2_a, euclidian).output("3.2a Gate_space", "3.2a : Min. Gate spacing : #{R3_2_a}um") +PL.enclosing(GATE, R3_3, projection).polygons.without_area(0).output("3.3 PL_enc_GATE", "3.3 : Min. Poly extention Gate : #{R3_3}um") +AA.enclosing(GATE, R3_4, projection).polygons.without_area(0).output("3.4 AA_enc_GATE", "3.4 : Min. Source/Drain length : #{R3_4}um") +PL.not(AA).separation(AA, 1, euclidian).polygons.without_area(0).output("3.5 PL_space_AA", "3.5 : Min. Poly on Field spacing Active : #{R3_5}um") + +### Poly2 +if !DEEP +PO2.ongrid(0.5*LAMBDA).output("POLY2_offgrid", "Offgrid vertex on Poly2") +PO2.with_angle(0 .. 45).output("POLY2_angle", "Non 45 degree angle Poly2") +PO2.width(R12_1, euclidian).output("12.1 POLY2_width", "12.1 : Min. Poly2 width : #{R12_1}um") +PO2.space(R12_2, euclidian).output("12.2 POLY2_space", "12.2 : Min. Poly2 space : #{R12_2}um") +# rule R12.3 not coded +PO2.not(AA).separation(AA, R12_4, euclidian).output("12.4 POLY2_space_AA"," 12.4 : Min. Poly2 on Field spacing Active : #{R12_4}um") +PO2.not(PL).separation(PL, R12_5, euclidian).output("12.5 POLY2_space_PL"," 12.5 : Min. Poly2 spacing Poly : #{R12_5}um") +PO2.enclosing(PL, R12_5, euclidian).output("12.5 POLY2_overlap_PL"," 12.5 : Min. Poly2 overlap of Poly : #{R12_5}um") +PO2.separation(AA.or(PL).and(CT), R12_6, euclidian).output("12.6 POLY2_space_CT"," 12.5 : Min. Poly2 spacing Poly or Active contact: #{R12_6}um") +PO2cap = PL & PO2 +PO2cap.width(R11_1, euclidian).output("11.1 POLY2CAP_width", "11.1 : Min. Poly2 Capacitor width : #{R11_1}um") +PO2cap.space(R11_2, euclidian).output("11.2 POLY2CAP_space", "11.2 : Min. Poly2 Capacitor space : #{R11_2}um") +PL.enclosing(PO2cap, R11_3, euclidian).output("12.3 PL_overlap_POLY2CAP"," 11.3 : Min. Poly overlap of Poly2 Capacitor : #{R11_3}um") +PO2cap.edges.separation(AA.or(NW).edges, R11_4, euclidian).output("11.4 POLY2CAP_space_AA/NW"," 11.4 : Min. Poly2 Capacitor spacing Active or Well: #{R11_4}um") +PO2cap.separation(PL.and(CT), R11_5, euclidian).output("11.5 POLY2CAP_space_PLCT"," 11.5 : Min. Poly2 Capacitor spacing Poly contact: #{R11_5}um") +PO2cap.separation(M1.or(M2).or(M3), R11_6, euclidian).output("11.6 POLY2CAP_space_METAL"," 11.6 : Min. Poly2 Capacitor spacing any Metal: #{R11_6}um") +PO2cap.forget + +### Capacitor Well +CW.ongrid(0.5*LAMBDA).output("CW_offgrid", "Offgrid vertex on CapacitorWell") +CW.with_angle(0 .. 45).output("CW_angle", "Non 45 degree angle CapacitorWell") +CW.width(R17_1, euclidian).output("17.1 CW_width", "17.1 : Min. CapacitorWell width : #{R17_1}um") +CW.space(R17_2, euclidian).output("17.2 CW_space", "17.2 : Min. CapacitorWell space : #{R17_2}um") +AA.not(CW).separation(CW, R17_3, euclidian).output("17.3 CW_space_AA"," 17.3 : Min. CapacitorWell spacing Active : #{R17_3}um") +CW.enclosing(AA, R17_4, euclidian).output("17.4 CW_overlap_AA"," 17.4 : Min. CapacitorWell overlap of Active : #{R17_4}um") +LinCap = PL & CW +LinCap.width(R18_1, euclidian).output("18.1 LC_width", "18.1 : Min. Linear Capacitor width : #{R18_1}um") +LinCap.space(R18_2, euclidian).output("18.2 LC_space", "18.2 : Min. Linear Capacitor space : #{R18_2}um") +LinCap.separation(AA.and(CT), R18_3, euclidian).output("18.3 LC_space_AACT"," 18.3 : Min. Linear Capacitor spacing Active contact : #{R18_3}um") +LinCap.separation(PL.and(CT), R18_4, euclidian).output("18.4 LC_space_PLCT"," 18.4 : Min. Linear Capacitor spacing Poly contact : #{R18_4}um") +LinCap.forget +end + +### N+/P+ Select +Nselect.ongrid(0.5*LAMBDA).output("NSel_offgrid", "Offgrid vertex on Nselect") +Pselect.ongrid(0.5*LAMBDA).output("PSel_offgrid", "Offgrid vertex on Pselect") +Nselect.with_angle(0 .. 45).output("N+_angle", "Non 45 degree angle Nselect") +Pselect.with_angle(0 .. 45).output("N+_angle", "Non 45 degree angle Pselect") +NP.enclosing(GATE, R4_1, projection).polygons.without_area(0).output("4.1 N+_enc_GATE", "4.1 : Min. N+ extention Gate on Source/Drain : #{R4_1}um") +PP.enclosing(GATE, R4_1, projection).polygons.without_area(0).output("4.1 P+_enc_GATE", "4.1 : Min. P+ extention Gate on Source/Drain : #{R4_1}um") +Nselect.enclosing(AA, R4_2, euclidian).output("4.2 N+_enc_AA", "4.2 : Min. N+ enclosing Active : #{R4_2}um") +Pselect.enclosing(AA, R4_2, euclidian).output("4.2 P+_enc_AA", "4.2 : Min. P+ enclosing Active : #{R4_2}um") +Nselect.enclosing(CT, R4_3, euclidian).output("4. N+_enc_CT", "4.3 : Min. N+ enclosing Contact : #{R4_3}um") +Pselect.enclosing(CT, R4_3, euclidian).output("4.3 P+_enc_CT", "4.3 : Min. P+ enclosing Contact : #{R4_3}um") +Nselect.width(R4_4,euclidian).output("4.4 N+_width", "4.4 : Min. N+ width : #{R4_4}um") +Pselect.width(R4_4,euclidian).output("4.4 P+_width", "4.4 : Min. N+ width : #{R4_4}um") +Nselect.space(R4_4,euclidian).output("4.4 N+_space", "4.4 : Min. N+ spacing : #{R4_4}um") +Pselect.space(R4_4,euclidian).output("4.4 P+_space", "4.4 : Min. N+ spacing : #{R4_4}um") +Nselect.and(Pselect).output("4.4 N+_and_P+", "4.4 : N+ over P+ not allowed") + +### HR - High Resistive +HR.ongrid(0.5*LAMBDA).output("HR_offgrid", "Offgrid vertex on HighRes") +HR.with_angle(0 .. 45).output("HR_angle", "Non 45 degree angle HighRes") +HR.width(R27_1, euclidian).output("27.1 HR_width", "27.1 : Min. HiRes width : #{R27_1}um") +HR.space(R27_2, euclidian).output("27.2 HR_space", "27.2 : Min. HiRes spacing : #{R27_2}um") +HR.and(CT).output("27.3 CT_and_HR", "27.3 : Contact on HiRes not allowed") +HR.separation(CT, R27_3, euclidian).output("27.3 HR_space_CT", "27.3 : Min. HiRes space to Contact : #{R27_3}um") +HR.separation(AA, R27_5, euclidian).output("27.4 HR_space_AA", "27.4 : Min. HiRes space to Active : #{R27_4}um") +HR.separation(PO2, R27_5, euclidian).output("27.5 HR_space_PO2", "27.5 : Min. HiRes space to Poly2 : #{R27_5}um") +HR.and(PO2).and(AA).output("27.6 HR_and_active", "27.6 : HiRes Po2 over Active not allowed") +HR.and(PO2).and(NW.or(PW)).output("27.6 HR_and_Well", "27.6 : HiRes Po2 over Well not allowed") +HR.and(PO2).width(R27_7,euclidian).output("27.7 HRPO2_width", "27.7 : Min. HiRes Poly2 width : #{R27_7}um") +HR.and(PO2).space(R27_8,euclidian).output("27.8 HRPO2_space", "27.8 : Min. HiRes Poly2 space : #{R27_8}um") +HR.enclosing(PO2, R27_9, projection).output("27.9 HR_enc_PO2", "27.9 : Min. HiRes enclosing Poly2 : #{R27_9}um") + +### SB - Silicide block +SB.ongrid(0.5*LAMBDA).output("SB_offgrid", "Offgrid vertex on Sil. Block") +SB.with_angle(0 .. 45).output("SB_angle", "Non 45 degree angle Sil. Block") +SB.width(R20_1, euclidian).output("20.1 SB_width", "20.1 : Min. Sil. Block width : #{R20_1}um") +SB.space(R20_2, euclidian).output("20.2 SB_space", "20.2 : Min. Sil. Block spacing : #{R20_2}um") +SB.separation(CT, R20_3, euclidian).output("20.3 SB_space_CT", "20.3 : Min. Sil. Block space to Contact : #{R20_3}um") +SB.and(CT).output("20.3 SB_and_CT", "20.3 : Sil. Block over Contact not allowed") +SB.separation(AA, R20_4, euclidian).output("20.4 SB_space_AA", "20.4 : Min. Sil. Block space to Active : #{R20_4}um") +SB.separation(PL, R20_5, euclidian).output("20.5 SB_space_PL", "20.5 : Min. Sil. Block space to Poly : #{R20_5}um") +SB.and(GATE).output("20.6 SBres_overAA","20.6 : SB resistor over Active not allowed") +SB.and(PL).and(Nselect.or(Pselect)).output("20.6 SBres_over_WELL","20.6 : SB resistor over Well not allowed") +SB.and(PL).width(R20_7,euclidian).output("20.6 SBres_width", "20.7 : Min. SB resistor width : #{R20_7}um") +SB.and(PL).space(R20_7,euclidian).output("20.7 SBres_space", "20.7 : Min. SB resistor space : #{R20_7}um") +SB.enclosing(AA, R20_8, projection).polygons.without_area(0).output("20.8 SB_enc_AA", "20.8 : Min. Sil. Block enclosing Active : #{R20_8}um") +AA.enclosing(SB, R20_9, projection).polygons.without_area(0).output("20.9 AA_enc_SB", "20.9 : Min. Active enclosing Sil. Block : #{R20_9}um") +SB.enclosing(PL, R20_9, projection).output("20.8 SB_enc_PL", "20.8 : Min. Sil. Block enclosing Poly : #{R20_8}um") +PL.enclosing(SB, R20_8, projection).polygons.without_area(0).output("20.9 PL_enc_SB", "20.9 : Min. Poly enclosing Sil. Block : #{R20_9}um") +SB.separation(GATE, R20_11, euclidian).output("20.11 SB_space_GATE", "20.11 : Min. Sil. Block space to Gate : #{R20_11}um") + +### Contact +CT.ongrid(0.5*LAMBDA).output("CT_offgrid", "Offgrid vertex on Contact") +CT.with_angle(0 .. 90).output("CT_angle", "Non 90 degree angle Contact") +CT.and(GATE).output("CT_and_GATE", "Contact on Gate not allowed") +CT.not(M1).output("CT_not_M1", "Contact without Metal1 not recommended") +# CT.drc(length != R5_1).output("5.1 CT_width", "5.1 : Exact Contact width : #{R5_1}um") +CT.width(R5_1).output("5.1 CT_width", "5.1 : Exact Contact width : #{R5_1}um") +CT.without_area(R5_1*R5_1).output("5.1 CT_area", "5.1 : Exact Contact Area : #{R5_1*R5_1}um2") +CT.space(R5_3, euclidian).output("5.3 CT_space", "5.3 : Contact spacing : #{R5_3}um") +PL.enclosing(CT, R5_2_b, euclidian).output("5.2 PL_enc_CT", "5.2 : Min. Poly enclosing Contact : #{R5_2_b}um") +AA.enclosing(CT, R6_2_b, euclidian).output("6.2 AA_enc_CT", "6.2 : Min. Active enclosing Contact : #{R6_2_b}um") +PO2.enclosing(CT, R13_3, euclidian).output("13.3 PO2_enc_CT", "13.3 : Min. Poly2 enclosing Contact : #{R13_3}um") +CT.and(AA).separation(GATE, R5_4, euclidian).output("5.4 CTAA_space_GATE", "5.4 : Min. ActiveContact space to Gate : #{R5_4}um") +CT.and(PL).separation(PL, R5_5_b, euclidian).polygons.without_area(0).output("5.5.b CTPL_space_Poly", "5.5.b : Min. PolyContact space to Poly : #{R5_5_b}um") +CT.and(PL).separation(AA, R5_6_b, euclidian).output("5.6.b CTPL_space_AA", "5.6.b : Min. PolyContact space to AA : #{R5_6_b}um") +# rule 5.7.b not coded +CT.and(AA).separation(AA, R6_5_b, euclidian).polygons.without_area(0).output("6.5.b CTAA_space_AA", "6.5.b : Min. ActiveContact space to AA : #{R6_5_b}um") +CT.and(AA).separation(PL, R6_6_b, euclidian).output("6.6.b CTAA_space_PL", "6.6.b : Min. ActiveContact space to Poly : #{R6_6_b}um") +CT.and(AA).separation(CT.and(PL), R6_7_b, euclidian).output("6.7.b CTAA_space_CTPL", "6.7.b : Min. ActiveContact space to PolyContact : #{R6_7_b}um") +# rule 6.8.b not coded +CT.and(PO2).separation(AA, R13_5, euclidian).output("13.5 CTPO2_space_AA", "13.5 : Min. Poly2Contact space to AA : #{R13_5}um") +CT.and(PO2).separation(PL, R13_5, euclidian).output("13.5 CTPO2_space_PL", "13.5 : Min. Poly2Contact space to Poly : #{R13_5}um") +M1.enclosing(CT, R7_3, euclidian).output("7.3 M1_enc_CT", "7.3 : Min. Metal1 enclosing Contact : #{R7_3}um") + +### Metal 1 +M1.ongrid(0.5*LAMBDA).output("M1_offgrid", "Offgrid vertex on ME1") +M1.with_angle(0 .. 45).output("M1_angle", "Non 45 degree angle ME1") +M1.holes.with_area(0 .. R7_1*R7_1).output("M1_holes", "Min. Metal1 holes area : #{R7_1*R7_1}um2") +M1.width(R7_1, euclidian).output("7.1 M1_width", "7.1 : Min. Metal1 width : #{R7_1}um") +M1.space(R7_2, euclidian).output("7.2 M1_space", "7.2 : Min. Metal1 spacing : #{R7_2}um") +M1.sized(-10*LAMBDA).sized(10*LAMBDA).separation(M1,R7_4,euclidian).polygons.without_area(0).output("7.4 M1_10_space", "7.4 : Space if at least one metal1 line width is > #{10*LAMBDA}um : #{R7_4}um") + +### Via 1 +V1.ongrid(0.5*LAMBDA).output("V1_offgrid", "Offgrid vertex on Via1") +V1.not(M1).output("V1_not_M1", "Via1 without Metal1 not allowed") +V1.not(M2).output("V1_not_M2", "Via1 without Metal2 not allowed") +V1.with_angle(0 .. 90).output("V1_angle", "Non 90 degree angle Via1") +V1.width(R8_1,square).output("8.1 V1_width", "8.1 : Exact Via1 width : #{R8_1}um") +V1.without_area(R8_1*R8_1).output("8.1 V1_width", "8.1 : Exact Via1 width : #{R8_1}um2") +V1.space(R8_2,euclidian).output("8.2 V1_space", "8.2 : Via1 spacing : #{R8_2}um") +M1.enclosing(V1, R8_3, euclidian).output("8.3 M1_enc_V1", "8.3 : Min. Metal1 enclosing Via1 : #{R8_3}um") +M2.enclosing(V1, R8_3, euclidian).output("9.3 M2_enc_V1", "9.3 : Min. Metal2 enclosing Via1 : #{R9_3}um") +if !DEEP && NBR_OF_METALS < 4 + V1.and(CT).or(V1.separation(CT, R8_4, euclidian).polygons).output("8.4 V1_space_CT", "8.4 : Via1 space CT : #{R8_4}um") + V1.separation(PL + AA, R8_5, euclidian).output("8.5 V1_space_PL/AAedges", "8.5 : Via1 space to Poly orActive edges : #{R8_5}um") +end +if DFM + M1.and(M2).not(V1).with_holes(1,1).output("2_V1", "Min. 2 Via1 are needed") +end + +### Metal 2 +M2.ongrid(0.5*LAMBDA).output("M2_offgrid", "Offgrid vertex on ME2") +M2.with_angle(0 .. 45).output("M2_angle", "Non 45 degree angle ME2") +M2.width(R9_1, euclidian).output("9.1 M2_width", "9.1 : Min. Metal2 width : #{R9_1}um") +M2.space(R9_2, euclidian).output("9.2 M2_space", "9.2 : Min. Metal2 spacing : #{R9_2}um") +M2.sized(-10*LAMBDA).sized(10*LAMBDA).separation(M2,R9_4,euclidian).polygons.without_area(0).output("9.4 M2_10_space", "9.4 : Space if at least one metal2 line width is > #{10*LAMBDA}um : #{R9_4}um") + +### Via 2 +V2.ongrid(0.5*LAMBDA).output("V2_offgrid", "Offgrid vertex on Via2") +V2.not(M3).output("V2_not_M3", "Via2 without Metal3 not allowed") +V2.not(M2).output("V2_not_M2", "Via2 without Metal2 not allowed") +V2.with_angle(0 .. 90).output("V2_angle", "Non 90 degree angle Via2") +V2.width(R14_1, square).output("14.1 V2_width", "14.1 : Exact Via2 width : #{R14_1}um") +V2.without_area(R14_1*R14_1).output("14.1 V2_width", "14.1 : Exact Via2 width : #{R14_1}um2") +V2.space(R14_2, euclidian).output("14.2 V2_space", "14.2 : Via2 spacing : #{R14_2}um") +M2.enclosing(V2, R14_3, euclidian).output("14.3 M2_enc_V2", "14.3 : Min. Metal2 enclosing Via2 : #{R14_3}um") +M3.enclosing(V2, R15_3, euclidian).output("15.3 M3_enc_V2", "15.3 : Min. Metal3 enclosing Via2 : #{R15_3}um") +if !DEEP && NBR_OF_METALS < 4 + V2.and(V1).or(V2.separation(V1, R14_4, euclidian).polygons).output("14.4 V2_space_V1", "14.4 : Via2 space Via1 : #{R14_4}um") +end +if DFM + M2.and(M3).not(V2).with_holes(1,1).output("2_V2", "Min. 2 Via2 are needed") +end + +### Metal 3 +M3.ongrid(0.5*LAMBDA).output("M3_offgrid", "Offgrid vertex on ME3") +M3.with_angle(0 .. 45).output("M3_angle", "Non 45 degree angle ME3") +M3.width(R15_1, euclidian).output("15.1 M3_width", "15.1 : Min. Metal3 width : #{R15_1}um") +M3.space(R15_2, euclidian).output("15.2 M3_space", "15.2 : Min. Metal3 spacing : #{R15_2}um") +M3.sized(-10*LAMBDA).sized(10*LAMBDA).separation(M3, R15_4, euclidian).polygons.without_area(0).output("15.4 M3_10_space", "15.4 : Space if at least one metal3 line width is > #{10*LAMBDA}um : #{R15_4}um") + +### Cap Top Metal +if SUBM || DEEP +CTM.ongrid(0.5*LAMBDA).output("CTM_offgrid", "Offgrid vertex on CTM") +CTM.with_angle(0 .. 45).output("CTM_angle", "Non 45 degree angle CTM") +CTM.width(R28_1, euclidian).output("28.1 CTM_width", "28.1 : Min. Cap Top Metal width : #{R28_1}um") +CTM.space(R28_2, euclidian).output("28.2 CTM_space", "28.2 : Min. Cap Top Metal spacing : #{R28_2}um") +if NBR_OF_METALS == 4 + TM = M4 + VT = V3 + TB = V2 +end +if NBR_OF_METALS == 5 + TM = M5 + VT = V4 + VB = V3 +end +if NBR_OF_METALS == 6 + TM = M6 + VT = V5 + VB = V4 +end +TM.enclosing(CTM, R28_3, euclidian).output("28.3 TM_overlap_CTM", "28.3 : Min. Top Metal overlap Cap Top Metal : #{R28_3}um") +CTM.enclosing(VT, R28_4, euclidian).output("28.4 CTM_overlap_VT", "28.4 : Min. Cap Top Metal overlap Top Via : #{R28_4}um") +CTM.separation(VB, R28_5, euclidian).output("28.5 CTM_space_VB", "28.5 : Min. Cap Top Metal space Bottom Via : #{R28_5}um") +CTM.separation(VT, R28_5, euclidian).output("28.5 CTM_space_VT", "28.5 : Min. Cap Top Metal space Top Via : #{R28_5}um") +TM.enclosing(VB, R28_6, euclidian).output("28.6 TM_overlap_VB", "28.6 : Min. Top Metal overlap Bottom Via : #{R28_6}um") +# rule 28.7 not coded +CTM.not_interacting(VT).width(R28_8, euclidian).output("28.8 CTMdummies_width", "28.8 : Min. dummies Cap Top Metal width : #{R28_8}um") +TM.interacting(CTM).space(R28_9, euclidian).output("28.9 VT_space", "28.9 : Min. Top Metal spacing : #{R28_9}um") +VT.interacting(CTM).space(R28_10, euclidian).output("28.10 VT_space", "28.10 : Min. Top Via spacing : #{R28_10}um") +VB.interacting(TM.interacting(CTM)).space(R28_11, euclidian).output("28.11 VB_space", "28.11 : Min. Bottom Via spacing : #{R28_11}um") +CTM.sized(-15.um).sized(15.um).output("28.12 CTM_width", "28.12 : Max. CTM width/length : 30um") +TM.interacting(CTM).sized(-17.5.um).sized(17.5.um).output("28.13 TM_width", "28.13 : Max. Top Metal width/length : 35um") +CTM.and(VB).output("28.14 CTM_VB", "28.14 : no VB under CTM allowed") +CTM.and(AA.or(PL)).output("28.15 CTM_AA", "28.15 : no active or passive circuitry under CTM allowed") +end + +### Via 3 +if NBR_OF_METALS > 3 +V3.ongrid(0.5*LAMBDA).output("V3_offgrid", "Offgrid vertex on Via3") +V3.not(M3).output("V3_not_M3", "Via3 without Metal3 not allowed") +V3.not(M4).output("V3_not_M4", "Via3 without Metal4 not allowed") +V3.with_angle(0 .. 90).output("V3_angle", "Non 90 degree angle Via3") +V3.width(R21_1, square).output("21.1 V3_width", "21.1 : Exact Via3 width : #{R21_1}um") +V3.without_area(R21_1*R21_1).output("21.1 V3_width", "21.1 : Exact Via3 width : #{R21_1}um2") +V3.space(R21_2, euclidian).output("21.2 V3_space", "21.2 : Via3 spacing : #{R21_2}um") +M3.enclosing(V3, R21_3, euclidian).output("21.3 M3_enc_V3", "21.3 : Min. Metal3 enclosing Via3 : #{R21_3}um") +M4.enclosing(V3, R22_3, euclidian).output("22.3 M4_enc_V3", "22.3 : Min. Metal4 enclosing Via3 : #{R22_3}um") +if DFM + M3.and(M4).not(V3).with_holes(1,1).output("2_V3", "Min. 2 Via3 are needed") +end + +### Metal 4 +M4.ongrid(0.5*LAMBDA).output("M4_offgrid", "Offgrid vertex on ME4") +M4.with_angle(0 .. 45).output("M4_angle", "Non 45 degree angle ME4") +M4.width(R22_1, euclidian).output("22.1 M4_width", "22.1 : Min. Metal4 width : #{R22_1}um") +M4.space(R22_2, euclidian).output("22.2 M4_space", "22.2 : Min. Metal4 spacing : #{R22_2}um") +M4.sized(-10*LAMBDA).sized(10*LAMBDA).separation(M4, R22_4, euclidian).polygons.without_area(0).output("22.4 M4_10_space", "22.4 : Space if at least one metal4 line width is > #{10*LAMBDA}um : #{R22_4}um") + +### Via 4 +if NBR_OF_METALS > 4 +V4.ongrid(0.5*LAMBDA).output("V4_offgrid", "Offgrid vertex on Via4") +V4.not(M5).output("V4_not_M5", "Via4 without Metal5 not allowed") +V4.not(M4).output("V4_not_M4", "Via4 without Metal4 not allowed") +V4.with_angle(0 .. 90).output("V4_angle", "Non 90 degree angle Via4") +V4.width(R25_1, square).output("25.1 V4_width", "25.1 : Exact Via4 width : #{R25_1}um") +V4.without_area(R25_1*R25_1).output("25.1 V4_width", "25.1 : Via4 width : #{R25_1}um2") +V4.space(R25_2, euclidian).output("25.2 V4_space", "25.2 : Exact Via4 spacing : #{R25_2}um") +M4.enclosing(V4, R25_3, euclidian).output("25.3 M4_enc_V4", "25.3 : Min. Metal4 enclosing Via4 : #{R25_3}um") +M5.enclosing(V4, R26_3, euclidian).output("26.3 M5_enc_V4", "26.3 : Min. Metal5 enclosing Via4 : #{R26_3}um") +if DFM + M4.and(M5).not(V4).with_holes(1,1).output("2_V4", "Min. 2 Via4 are needed") +end + +### Metal 5 +M5.ongrid(0.5*LAMBDA).output("M5_offgrid", "Offgrid vertex on ME5") +M5.with_angle(0 .. 45).output("M5_angle", "Non 45 degree angle ME5") +M5.width(R26_1, euclidian).output("26.1 M5_width", "26.1 : Min. Metal5 width : #{R26_1}um") +M5.space(R26_2, euclidian).output("26.2 M5_space", "26.2 : Min. Metal5 spacing : #{R26_2}um") +M5.sized(-10*LAMBDA).sized(10*LAMBDA).separation(M5, R26_4, euclidian).polygons.without_area(0).output("26.4 M5_10_space", "26 .4 : Space if at least one metal5 line width is > #{10*LAMBDA}um : #{R26_4}um") + +### Via 5 +if NBR_OF_METALS > 5 +V5.ongrid(0.5*LAMBDA).output("V5_offgrid", "Offgrid vertex on Via5") +V5.not(M5).output("V5_not_M5", "Via5 without Metal5 not allowed") +V5.not(M6).output("V5_not_M6", "Via5 without Metal6 not allowed") +V5.with_angle(0 .. 90).output("V4_angle", "Non 90 degree angle Via5") +V5.width(R29_1, square).output("29.1 V5_width", "29.1 : Exact Via5 width : #{R29_1}um") +V5.without_area(R29_1*R29_1).output("29.1 V5_width", "29.1 : Exact Via5 width : #{R29_1}um2") +V5.space(R29_2, euclidian).output("29.2 V5_space", "29.2 : Via5 spacing : #{R29_2}um") +M5.enclosing(V5, R29_3, euclidian).output("29.3 M5_enc_V5", "29.3 : Min. Metal5 enclosing Via5 : #{R29_3}um") +M6.enclosing(V5, R30_3, euclidian).output("30.3 M3_enc_V5", "30.3 : Min. Metal6 enclosing Via5 : #{R30_3}um") +if DFM + M5.and(M6).not(V5).with_holes(1,1).output("2_V5", "Min. 2 Via5 are needed") +end + +### Metal 6 +M6.ongrid(0.5*LAMBDA).output("M6_offgrid", "Offgrid vertex on ME6") +M6.with_angle(0 .. 45).output("M6_angle", "Non 45 degree angle ME5") +M6.width(R30_1, euclidian).output("30.1 M6_width", "30.1 : Min. Metal6 width : #{R30_1}um") +M6.space(R30_2, euclidian).output("30.2 M6_space", "30.2 : Min. Metal6 spacing : #{R30_2}um") +M6.sized(-10*LAMBDA).sized(10*LAMBDA).separation(M5, 10, euclidian).polygons.without_area(0).output("30.4 M6_10_space", "30.4 : Space if at least one metal6 line width is > #{10*LAMBDA}um : #{R30_4}um") + +end +end +end + +# time spent for the DRC +time = Time.now +hours = ((time - tstart)/3600).to_i +minutes = ((time - tstart)/60 - hours * 60).to_i +seconds = ((time - tstart) - (minutes * 60 + hours * 3600)).to_i +$stdout.write "DRC finished at : #{time.hour}:#{time.min}:#{time.sec} - DRC duration = #{hours} hrs. #{minutes} min. #{seconds} sec.\n" + diff --git a/technology/scn4m_subm/tech/scn4m_subm.lylvs b/technology/scn4m_subm/tech/scn4m_subm.lylvs new file mode 100644 index 00000000..80185ede --- /dev/null +++ b/technology/scn4m_subm/tech/scn4m_subm.lylvs @@ -0,0 +1,208 @@ + + + + + lvs + + + + false + false + + true + lvs_scripts + tools_menu.lvs.end + dsl + lvs-dsl-xml + # +# Extraction for freePDK45 +# +############################ +tstart = Time.now + +# optionnal for a batch launch : klayout -b -rd input=my_layout.gds -rd report=my_report.lyrdb -rd schematic=reference_netlist.cir -rd target_netlist=extracted_netlist.cir -r lvs_freepdk45.lvs +if $input + source($input) +end + +if $report + report_lvs($report) +else + report_lvs("lvs_report.lvsdb") +end + +if $schematic +#reference netlist + schematic($schematic) +else + schematic(RBA::CellView::active.filename.sub(/\.(oas|gds|oas.gz|gds.gz)$/, ".sp")) +end + +# true: use net names instead of numbers +# false: use numbers for nets +spice_with_net_names = true + +# true: put in comments with details +# false: no comments +spice_with_comments = true + +if $target_netlist + target_netlist($target_netlist) +else + # target_netlist("netlist.cir", write_spice(spice_with_net_names, spice_with_comments), "The netlist comment goes here.") + target_netlist(File.join(File.dirname(RBA::CellView::active.filename), source.cell_name+"_extracted.cir"), write_spice(spice_with_net_names, spice_with_comments), "Extracted by KLayout on : #{Time.now.strftime("%d/%m/%Y %H:%M")}") +end + +# Hierarchical mode +deep +# Use 4 CPU cores +threads(4) +# Print details +verbose(true) + + +# layers definitions +######################## +info("Layers definitions") + + DNW = input(38,0) + nwell = input(42,0) + pwell = input(41,0) + CW = input(59,0) + active = input(43,0) + TA = input(60,0) + PBase = input(58,0) + poly = input(46,0) + SB = input(29,0) + nplus = input(45,0) + pplus = input(44,0) + PO2 = input(56,0) + HR = input(34,0) + Contact = input(25,0) + ContactPoly = input(47,0) + ContactActive = input(48,0) + ContactPoly2 = input(55, 0) + CT = Contact + ContactPoly + ContactActive + ContactPoly2 + M1 = input(49,0) + V1 = input(50,0) + M2 = input(51,0) + V2 = input(61,0) + M3 = input(62,0) + V3 = input(30,0) + M4 = input(31,0) + CTM = input(35,0) + V4 = input(32,0) + M5 = input(33,0) + V5 = input(36,0) + M6 = input(37,0) + Glass = input(52,0) + Pads = input(26,0) + +# layers processing +######################## +info("Layers processing") + +# Bulk layer for terminal provisioning +bulk = polygon_layer + +active_in_nwell = active & nwell +pactive = active_in_nwell & pplus +ntie = active_in_nwell & nplus +pgate = pactive & poly +psd = pactive - pgate + +active_in_pwell = active & pwell +nactive = active_in_pwell & nplus +ptie = active_in_pwell & pplus +ngate = nactive & poly +nsd = nactive - ngate + + +cheat("cell_1rw", "dummy_cell_1rw", "replica_cell_1rw", "cell_2rw", "dummy_cell_2rw", "replica_cell_2rw", "pbitcell", "dummy_pbitcell", "replica_pbitcell", "dff", "wordline_driver*") { + +# PMOS transistor device extraction +extract_devices(mos4("p"), { "SD" => psd, "G" => pgate, "tS" => psd, "tD" => psd, "tG" => poly, "W" => nwell }) + +# NMOS transistor device extraction +extract_devices(mos4("n"), { "SD" => nsd, "G" => ngate, "tS" => nsd, "tD" => nsd, "tG" => poly, "W" => pwell }) + +} + +# Define connectivity for netlist extraction + +# Inter-layer +connect(nwell, ntie) +connect(pwell, ptie) +connect(CT, ntie) +connect(CT, ptie) +connect(psd, CT) +connect(nsd, CT) +connect(poly, CT) +connect(CT, M1) +connect(CT, M1) +connect(M1, V1) +connect(V1, M2) +connect(M2, V2) +connect(V2, M3) +connect(M3, V3) +connect(V3, M4) +connect(M4, V4) +connect(V4, M5) +connect(M5, V5) +connect(V5, M6) + + +# Global +schematic.simplify + +if $connect_supplies + connect_implicit("vdd") + connect_implicit("gnd") +end + +connect_global(pwell, "PWELL") +connect_global(nwell, "NWELL") +#connect_global(bulk, "BULK") + +for pat in %w(pinv* pnor* pnand* and?_dec* write_driver* port_address* replica_bitcell_array*) + connect_explicit(pat, [ "NWELL", "vdd" ]) + connect_explicit(pat, [ "BULK", "PWELL", "gnd" ]) +end + +# Actually performs the extraction +netlist # ... not really required + +# Flatten cells which are present in one netlist only +align +# SIMPLIFICATION of the netlist +#netlist.make_top_level_pins +#netlist.combine_devices +#netlist.purge +#netlist.purge_nets +netlist.simplify + +# Tolerances for the devices extracted parameters +# tolerance(device_class_name, parameter_name [, :absolute => absolute_tolerance] [, :relative => relative_tolerance]) +tolerance("P", "W", :absolute => 1.nm, :relative => 0.001) +tolerance("N", "W", :absolute => 1.nm, :relative => 0.001) + +#max_res(1000000) +#min_caps(1e-15) + +max_branch_complexity(65536) +max_depth(16) + +if ! compare + #raise "ERROR : Netlists don't match" + puts "ERROR : Netlists don't match" +else + puts "CONGRATULATIONS! Netlists match." +end + +# time spent for the LVS +time = Time.now +hours = ((time - tstart)/3600).to_i +minutes = ((time - tstart)/60 - hours * 60).to_i +seconds = ((time - tstart) - (minutes * 60 + hours * 3600)).to_i +$stdout.write "LVS finished at : #{time.hour}:#{time.min}:#{time.sec} - LVS duration = #{hours} hrs. #{minutes} min. #{seconds} sec.\n" + diff --git a/technology/scn4m_subm/tech/tech.py b/technology/scn4m_subm/tech/tech.py index a8c44996..e940ce29 100644 --- a/technology/scn4m_subm/tech/tech.py +++ b/technology/scn4m_subm/tech/tech.py @@ -6,10 +6,11 @@ # All rights reserved. # import os -from design_rules import * -from module_type import * -from custom_cell_properties import cell_properties -from custom_layer_properties import layer_properties +import drc as d +#from drc.design_rules import design_rules +#from drc.module_type import module_type +#from drc.custom_cell_properties import cell_properties +#from drc.custom_layer_properties import layer_properties """ File containing the process technology parameters for SCMOS 4m, 0.35um @@ -24,17 +25,19 @@ File containing the process technology parameters for SCMOS 4m, 0.35um # Using tech_modules['cellname'] you can override each class by providing a custom # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules['contact'] = 'contact_scn4m' -tech_modules = module_type() +tech_modules = d.module_type() ################################################### # Custom cell properties ################################################### -cell_properties = cell_properties() +cell_properties = d.cell_properties() +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "V" ################################################### # Custom cell properties ################################################### -layer_properties = layer_properties() +layer_properties = d.layer_properties() ################################################### # GDS file info @@ -158,7 +161,7 @@ parameter["6T_access_size"] = 4*_lambda_ drclvs_home=os.environ.get("DRCLVS_HOME") -drc = design_rules("scn4me_sub") +drc = d.design_rules("scn4me_sub") #grid size is 1/2 a lambda drc["grid"]=0.5*_lambda_ diff --git a/technology/sky130/__init__.py b/technology/sky130/__init__.py index 3df3f555..409f0032 100644 --- a/technology/sky130/__init__.py +++ b/technology/sky130/__init__.py @@ -25,9 +25,9 @@ if 'PDK_ROOT' in os.environ: else: raise SystemError("Unable to find open_pdks tech file. Set PDK_ROOT.") -spice_model_dir = os.path.join(open_pdks, "SIMULATOR",) +# The ngspice models work with Xyce too now +spice_model_dir = os.path.join(open_pdks, "ngspice") sky130_lib_ngspice = os.path.join(open_pdks, "ngspice", "sky130.lib.spice") -# We may end up using Xyce but check if at least ngspice exists if not os.path.exists(sky130_lib_ngspice): raise SystemError("Did not find {} under {}".format(sky130_lib_ngspice, open_pdks)) os.environ["SPICE_MODEL_DIR"] = spice_model_dir diff --git a/technology/sky130/custom/__init__.py b/technology/sky130/custom/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/technology/sky130/modules/sky130_bitcell.py b/technology/sky130/custom/sky130_bitcell.py similarity index 94% rename from technology/sky130/modules/sky130_bitcell.py rename to technology/sky130/custom/sky130_bitcell.py index 90371a29..908ff45d 100644 --- a/technology/sky130/modules/sky130_bitcell.py +++ b/technology/sky130/custom/sky130_bitcell.py @@ -7,10 +7,10 @@ import debug from tech import cell_properties as props -import bitcell_base +from modules import bitcell_base -class sky130_bitcell(bitcell_base.bitcell_base): +class sky130_bitcell(bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It is a hand-made cell, so diff --git a/technology/sky130/modules/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py similarity index 77% rename from technology/sky130/modules/sky130_bitcell_array.py rename to technology/sky130/custom/sky130_bitcell_array.py index 5d250f15..2c7f5cd5 100644 --- a/technology/sky130/modules/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -6,8 +6,8 @@ # import debug -from bitcell_array import bitcell_array -from sky130_bitcell_base_array import sky130_bitcell_base_array +from modules import bitcell_array +from .sky130_bitcell_base_array import sky130_bitcell_base_array from globals import OPTS from sram_factory import factory @@ -38,15 +38,11 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Add the modules used in this design """ # Bitcell for port names only self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") - self.add_mod(self.cell) self.cell2 = factory.create(module_type=OPTS.bitcell, version="opt1a") - self.add_mod(self.cell2) self.strap = factory.create(module_type="internal", version="wlstrap") - self.add_mod(self.strap) self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.add_mod(self.strap2) self.strap3 = factory.create(module_type="internal", version="wlstrapa") - self.add_mod(self.strap3) + self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") def create_instances(self): """ Create the module instances used in this design """ @@ -67,25 +63,29 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): row_layout.append(self.cell2) self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), mod=self.cell2) - self.connect_inst(self.get_bitcell_pins(row, col)) if col != self.column_size - 1: if alternate_strap: - row_layout.append(self.strap2) - self.add_inst(name="row_{}_col_{}_wlstrap".format(row, col), - mod=self.strap2) + if row % 2: + name="row_{}_col_{}_wlstrapa_p".format(row, col) + row_layout.append(self.strap4) + self.add_inst(name=name, mod=self.strap4) + else: + name="row_{}_col_{}_wlstrap_p".format(row, col) + row_layout.append(self.strap2) + self.add_inst(name=name, mod=self.strap2) alternate_strap = 0 else: if row % 2: + name="row_{}_col_{}_wlstrapa".format(row, col) row_layout.append(self.strap3) - self.add_inst(name="row_{}_col_{}_wlstrap".format(row, col), - mod=self.strap3) + self.add_inst(name=name.format(row, col), mod=self.strap3) else: + name="row_{}_col_{}_wlstrap".format(row, col) row_layout.append(self.strap) - self.add_inst(name="row_{}_col_{}_wlstrap".format(row, col), - mod=self.strap) + self.add_inst(name=name.format(row, col), mod=self.strap) alternate_strap = 1 - self.connect_inst(self.get_strap_pins(row, col)) + self.connect_inst(self.get_strap_pins(row, col, name)) if alternate_bitcell == 0: alternate_bitcell = 1 else: diff --git a/technology/sky130/modules/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py similarity index 75% rename from technology/sky130/modules/sky130_bitcell_base_array.py rename to technology/sky130/custom/sky130_bitcell_base_array.py index 5b0e39d4..1604fa02 100644 --- a/technology/sky130/modules/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -6,9 +6,9 @@ # import debug -import geometry +from base import geometry from sram_factory import factory -from bitcell_base_array import bitcell_base_array +from modules import bitcell_base_array from globals import OPTS from tech import layer @@ -61,7 +61,7 @@ class sky130_bitcell_base_array(bitcell_base_array): self.width = max([x.rx() for x in self.insts]) self.height = max([x.uy() for x in self.insts]) - def get_bitcell_pins(self, row, col): + def get_bitcell_pins(self, row, col, swap = False): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array @@ -77,21 +77,24 @@ class sky130_bitcell_base_array(bitcell_base_array): return bitcell_pins - def get_strap_pins(self, row, col): + def get_strap_pins(self, row, col, name=""): """ Creates a list of connections in the strap cell, indexed by column and row, for instance use in bitcell_array """ - strap_pins = ["vdd"] - return strap_pins - - def get_col_cap_pins(self, row, col): - """ - """ - strap_pins = ["gnd", "gnd", "vdd"] + if name and "_p" in name: + strap_pins = ["gnd"] + else: + strap_pins = ["vdd"] return strap_pins def get_col_cap_p_pins(self, row, col): + """ + """ + strap_pins = ["gnd", "vdd", "gnd"] + return strap_pins + + def get_col_cap_pins(self, row, col): """ """ strap_pins = [] @@ -100,6 +103,11 @@ class sky130_bitcell_base_array(bitcell_base_array): strap_pins.extend(["vdd", "gnd"]) for port in self.all_ports: strap_pins.extend([x for x in self.get_bitline_names(port) if "br" in x and x.endswith("_{0}".format(col))]) + if row == 0: + strap_pins.extend(["top_gate"]) + else: + strap_pins.extend(["bot_gate"]) + strap_pins.extend(["vdd", "gnd"]) return strap_pins def get_row_cap_pins(self, row, col): @@ -124,7 +132,7 @@ class sky130_bitcell_base_array(bitcell_base_array): self.copy_layout_pin(inst, pin_name) if row == 2: #add only 1 label per col - if 'VPB' in self.cell_inst[row, col].mod.pins: + if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: pin = inst.get_pin("vpb") self.objs.append(geometry.rectangle(layer["nwell"], pin.ll(), @@ -132,7 +140,7 @@ class sky130_bitcell_base_array(bitcell_base_array): pin.height())) self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - if 'VNB' in self.cell_inst[row, col].mod.pins: + if 'VNB' or 'vnb'in self.cell_inst[row, col].mod.pins: try: from tech import layer_override if layer_override['VNB']: @@ -145,3 +153,26 @@ class sky130_bitcell_base_array(bitcell_base_array): except: pin = inst.get_pin("vnb") self.add_label("vdd", pin.layer, pin.center()) + + def add_bitline_pins(self): + bitline_names = self.cell.get_all_bitline_names() + for col in range(self.column_size): + for port in self.all_ports: + bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) + text = "bl_{0}_{1}".format(port, col) + #if "Y" in self.cell_inst[0, col].mirror: + # text = text.replace("bl", "br") + self.add_layout_pin(text=text, + layer=bl_pin.layer, + offset=bl_pin.ll().scale(1, 0), + width=bl_pin.width(), + height=self.height) + br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) + text = "br_{0}_{1}".format(port, col) + #if "Y" in self.cell_inst[0, col].mirror: + # text = text.replace("br", "bl") + self.add_layout_pin(text=text, + layer=br_pin.layer, + offset=br_pin.ll().scale(1, 0), + width=br_pin.width(), + height=self.height) diff --git a/technology/sky130/modules/sky130_col_cap.py b/technology/sky130/custom/sky130_col_cap.py similarity index 96% rename from technology/sky130/modules/sky130_col_cap.py rename to technology/sky130/custom/sky130_col_cap.py index cd328754..eb2383e5 100644 --- a/technology/sky130/modules/sky130_col_cap.py +++ b/technology/sky130/custom/sky130_col_cap.py @@ -6,11 +6,11 @@ # import debug -import design +from base import design from tech import cell_properties as props -class sky130_col_cap(design.design): +class sky130_col_cap(design): def __init__(self, version, name=""): if version == "colend": diff --git a/technology/sky130/modules/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py similarity index 68% rename from technology/sky130/modules/sky130_col_cap_array.py rename to technology/sky130/custom/sky130_col_cap_array.py index b1e9e35b..940296d3 100644 --- a/technology/sky130/modules/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -6,9 +6,10 @@ # from sram_factory import factory -from sky130_bitcell_base_array import sky130_bitcell_base_array +from .sky130_bitcell_base_array import sky130_bitcell_base_array from globals import OPTS - +from base import geometry +from tech import layer class sky130_col_cap_array(sky130_bitcell_base_array): """ @@ -41,7 +42,7 @@ class sky130_col_cap_array(sky130_bitcell_base_array): self.place_array("dummy_r{0}_c{1}", self.mirror) self.add_layout_pins() - + self.add_supply_pins() self.add_boundary() self.DRC_LVS() @@ -49,18 +50,12 @@ class sky130_col_cap_array(sky130_bitcell_base_array): """ Add the modules used in this design """ if self.location == "top": self.colend1 = factory.create(module_type="col_cap", version="colend") - self.add_mod(self.colend1) self.colend2 = factory.create(module_type="col_cap", version="colend_p_cent") - self.add_mod(self.colend2) self.colend3 = factory.create(module_type="col_cap", version="colend_cent") - self.add_mod(self.colend3) elif self.location == "bottom": self.colend1 = factory.create(module_type="col_cap", version="colenda") - self.add_mod(self.colend1) self.colend2 = factory.create(module_type="col_cap", version="colenda_p_cent") - self.add_mod(self.colend2) self.colend3 = factory.create(module_type="col_cap", version="colenda_cent") - self.add_mod(self.colend3) self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") @@ -83,6 +78,9 @@ class sky130_col_cap_array(sky130_bitcell_base_array): pins.append("vdd") pins.append("gnd") pins.append("fake_br_{}".format(bitline)) + pins.append("gate") + pins.append("vdd") + pins.append("gnd") bitline += 1 elif col % 4 == 1: row_layout.append(self.colend2) @@ -97,13 +95,16 @@ class sky130_col_cap_array(sky130_bitcell_base_array): pins.append("vdd") pins.append("gnd") pins.append("fake_br_{}".format(bitline)) + pins.append("gate") + pins.append("vdd") + pins.append("gnd") bitline += 1 elif col % 4 ==3: row_layout.append(self.colend2) self.cell_inst[col]=self.add_inst(name=name, mod=self.colend2) pins.append("gnd") pins.append("vdd") - pins.append("gnd") + pins.append("vnb") self.connect_inst(pins) @@ -134,9 +135,11 @@ class sky130_col_cap_array(sky130_bitcell_base_array): for fake_bl in range(self.cols): self.add_pin("fake_bl_{}".format(fake_bl), "OUTPUT") self.add_pin("fake_br_{}".format(fake_bl), "OUTPUT") - self.add_pin("fake_wl", "INPUT") + #self.add_pin("fake_wl", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") + self.add_pin("gate", "BIAS") + def add_layout_pins(self): """ Add the layout pins """ @@ -147,32 +150,99 @@ class sky130_col_cap_array(sky130_bitcell_base_array): for pin in inst.get_pins(pin_name): if inst.mod.cell_name == 'sky130_fd_bd_sram__sram_sp_colend' or 'sky130_fd_bd_sram__sram_sp_colenda': if inst.mirror == "MY": - if pin_name == "vdd": + if pin_name == "vdd" and pin.layer == 'm1': self.add_layout_pin_rect_center(text="vdd", layer=pin.layer, offset=inst.lr(), width=pin.width(), height=pin.height()) - elif pin_name == "gnd": + elif pin_name == "gnd" and pin.layer == 'm1': self.add_layout_pin_rect_center(text="gnd", layer=pin.layer, offset=inst.ll(), width=pin.width(), height=pin.height()) else: - if pin_name == "vdd": + if pin_name == "vdd" and pin.layer == 'm1': self.add_layout_pin_rect_center(text="vdd", layer=pin.layer, offset=inst.ll(), width=pin.width(), height=pin.height()) - elif pin_name == "gnd": + elif pin_name == "gnd" and pin.layer == 'm1': self.add_layout_pin_rect_center(text="gnd", layer=pin.layer, offset=inst.lr(), width=pin.width(), height=pin.height()) + + + for col in range(len(self.insts)): + + inst = self.insts[col] + if col % 4 == 0: + pin = self.cell_inst[col].get_pin("bl") + text = "fake_bl_{}".format(int(col/2)) + self.add_layout_pin(text=text, + layer=pin.layer, + offset=pin.ll().scale(1, 0), + width=pin.width(), + height=pin.height()) + + pin = self.cell_inst[col].get_pin("br") + text = "fake_br_{}".format(int(col/2)) + self.add_layout_pin(text=text, + layer=pin.layer, + offset=pin.ll().scale(1, 0), + width=pin.width(), + height=pin.height()) + + elif col % 4 == 2: + pin = self.cell_inst[col].get_pin("bl") + text = "fake_bl_{}".format(int(col/2)) + self.add_layout_pin(text=text, + layer=pin.layer, + offset=pin.ll().scale(1, 0), + width=pin.width(), + height=pin.height()) + + pin = self.cell_inst[col].get_pin("br") + text = "fake_br_{}".format(int(col/2)) + self.add_layout_pin(text=text, + layer=pin.layer, + offset=pin.ll().scale(1, 0), + width=pin.width(), + height=pin.height()) return + + def add_supply_pins(self): + for col in range(len(self.insts)): + inst = self.cell_inst[col] + + if 'VPB' or 'vnb' in self.cell_inst[col].mod.pins: + pin = inst.get_pin("vpb") + self.objs.append(geometry.rectangle(layer["nwell"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + + + if 'VNB' or 'vnb' in self.cell_inst[col].mod.pins: + try: + from tech import layer_override + if layer_override['VNB']: + pin = inst.get_pin("vnb") + self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + self.objs.append(geometry.rectangle(layer["pwellp"], + pin.ll(), + pin.width(), + pin.height())) + except: + pin = inst.get_pin("vnb") + self.add_label("vdd", pin.layer, pin.center()) + + def create_all_wordline_names(self, row_size=None): if row_size == None: diff --git a/technology/sky130/modules/sky130_corner.py b/technology/sky130/custom/sky130_corner.py similarity index 63% rename from technology/sky130/modules/sky130_corner.py rename to technology/sky130/custom/sky130_corner.py index 4c5594a2..858a0a4a 100644 --- a/technology/sky130/modules/sky130_corner.py +++ b/technology/sky130/custom/sky130_corner.py @@ -6,12 +6,12 @@ # import debug -import design -import utils +from base import design +from base import get_libcell_size from tech import layer, GDS -class sky130_corner(design.design): +class sky130_corner(design): def __init__(self, location, name=""): super().__init__(name) @@ -26,8 +26,8 @@ class sky130_corner(design.design): self.name = "sky130_fd_bd_sram__sram_sp_cornera" else: debug.error("Invalid sky130_corner location", -1) - design.design.__init__(self, name=self.name) - (self.width, self.height) = utils.get_libcell_size(self.name, - GDS["unit"], - layer["mem"]) - # pin_map = utils.get_libcell_pins(pin_names, self.name, GDS["unit"]) + design.__init__(self, name=self.name) + (self.width, self.height) = get_libcell_size(self.name, + GDS["unit"], + layer["mem"]) + # pin_map = get_libcell_pins(pin_names, self.name, GDS["unit"]) diff --git a/technology/sky130/modules/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py similarity index 63% rename from technology/sky130/modules/sky130_dummy_array.py rename to technology/sky130/custom/sky130_dummy_array.py index 9f854dd8..bfdca620 100644 --- a/technology/sky130/modules/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -5,10 +5,11 @@ # All rights reserved. # -from sky130_bitcell_base_array import sky130_bitcell_base_array +from .sky130_bitcell_base_array import sky130_bitcell_base_array from sram_factory import factory from globals import OPTS - +from base import geometry +from tech import layer class sky130_dummy_array(sky130_bitcell_base_array): """ @@ -37,7 +38,7 @@ class sky130_dummy_array(sky130_bitcell_base_array): self.place_array("dummy_r{0}_c{1}", self.mirror) self.add_layout_pins() - + self.add_supply_pins() self.add_boundary() self.DRC_LVS() @@ -45,13 +46,11 @@ class sky130_dummy_array(sky130_bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell, version="opt1") - self.add_mod(self.dummy_cell) self.dummy_cell2 = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a") - self.add_mod(self.dummy_cell2) self.strap = factory.create(module_type="internal", version="wlstrap") - self.add_mod(self.strap) self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.add_mod(self.strap2) + self.strap3 = factory.create(module_type="internal", version="wlstrapa") + self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") def create_instances(self): @@ -73,21 +72,34 @@ class sky130_dummy_array(sky130_bitcell_base_array): row_layout.append(self.dummy_cell2) self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), mod=self.dummy_cell2) - self.connect_inst(self.get_bitcell_pins(row, col)) if col != self.column_size - 1: if alternate_strap: - row_layout.append(self.strap2) - self.add_inst(name="row_{}_col_{}_wlstrap".format(row, col), - mod=self.strap2) + if col % 2: + name="row_{}_col_{}_wlstrap_p".format(row, col) + row_layout.append(self.strap4) + self.add_inst(name=name, + mod=self.strap4) + else: + name="row_{}_col_{}_wlstrapa_p".format(row, col) + row_layout.append(self.strap2) + self.add_inst(name=name, + mod=self.strap2) alternate_strap = 0 else: - - row_layout.append(self.strap) - self.add_inst(name="row_{}_col_{}_wlstrap".format(row, col), - mod=self.strap) + if col % 2: + name="row_{}_col_{}_wlstrap".format(row, col) + row_layout.append(self.strap) + self.add_inst(name=name, + mod=self.strap) + else: + name="row_{}_col_{}_wlstrapa".format(row, col) + row_layout.append(self.strap3) + self.add_inst(name=name, + mod=self.strap3) alternate_strap = 1 - self.connect_inst(self.get_strap_pins(row, col)) + + self.connect_inst(self.get_strap_pins(row, col, name)) if alternate_bitcell == 0: alternate_bitcell = 1 else: @@ -96,13 +108,15 @@ class sky130_dummy_array(sky130_bitcell_base_array): def add_pins(self): # bitline pins are not added because they are floating + for bl in range(self.column_size): + self.add_pin("bl_0_{}".format(bl)) + self.add_pin("br_0_{}".format(bl)) for wl_name in self.get_wordline_names(): self.add_pin(wl_name, "INPUT") - for bl in range(self.column_size): - self.add_pin("dummy_bl_{}".format(bl)) - self.add_pin("dummy_br_{}".format(bl)) self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") + #self.add_pin("vpb", "BIAS") + #Sself.add_pin("vnb", "BIAS") def add_layout_pins(self): """ Add the layout pins """ @@ -110,13 +124,15 @@ class sky130_dummy_array(sky130_bitcell_base_array): for col in range(self.column_size): for port in self.all_ports: bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) - self.add_layout_pin(text="bl_{0}_{1}".format(port, col), + text = "bl_{0}_{1}".format(port, col) + self.add_layout_pin(text=text, layer=bl_pin.layer, offset=bl_pin.ll().scale(1, 0), width=bl_pin.width(), height=self.height) br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) - self.add_layout_pin(text="br_{0}_{1}".format(port, col), + text = "br_{0}_{1}".format(port, col) + self.add_layout_pin(text=text, layer=br_pin.layer, offset=br_pin.ll().scale(1, 0), width=br_pin.width(), @@ -147,6 +163,32 @@ class sky130_dummy_array(sky130_bitcell_base_array): for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) + def add_supply_pins(self): + for row in range(self.row_size): + for col in range(self.column_size): + inst = self.cell_inst[row, col] + if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: + pin = inst.get_pin("vpb") + self.objs.append(geometry.rectangle(layer["nwell"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + + if 'VNB' or 'vnb' in self.cell_inst[row, col].mod.pins: + try: + from tech import layer_override + if layer_override['VNB']: + pin = inst.get_pin("vnb") + self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + self.objs.append(geometry.rectangle(layer["pwellp"], + pin.ll(), + pin.width(), + pin.height())) + except: + pin = inst.get_pin("vnb") + self.add_label("vdd", pin.layer, pin.center()) + def input_load(self): # FIXME: This appears to be old code from previous characterization. Needs to be updated. wl_wire = self.gen_wl_wire() diff --git a/technology/sky130/modules/sky130_dummy_bitcell.py b/technology/sky130/custom/sky130_dummy_bitcell.py similarity index 91% rename from technology/sky130/modules/sky130_dummy_bitcell.py rename to technology/sky130/custom/sky130_dummy_bitcell.py index 044c2848..58ef8026 100644 --- a/technology/sky130/modules/sky130_dummy_bitcell.py +++ b/technology/sky130/custom/sky130_dummy_bitcell.py @@ -7,10 +7,10 @@ import debug from tech import cell_properties as props -import bitcell_base +from modules import bitcell_base -class sky130_dummy_bitcell(bitcell_base.bitcell_base): +class sky130_dummy_bitcell(bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It is a hand-made cell, so diff --git a/technology/sky130/modules/sky130_internal.py b/technology/sky130/custom/sky130_internal.py similarity index 55% rename from technology/sky130/modules/sky130_internal.py rename to technology/sky130/custom/sky130_internal.py index c481195f..10637384 100644 --- a/technology/sky130/modules/sky130_internal.py +++ b/technology/sky130/custom/sky130_internal.py @@ -6,12 +6,12 @@ # import debug -import design -import utils +from base import design +from base import get_libcell_size from tech import layer, GDS -class sky130_internal(design.design): +class sky130_internal(design): def __init__(self, version, name=""): super().__init__(name) @@ -22,10 +22,12 @@ class sky130_internal(design.design): self.name = "sky130_fd_bd_sram__sram_sp_wlstrap_p" elif version == "wlstrapa": self.name = "sky130_fd_bd_sram__sram_sp_wlstrapa" + elif version == "wlstrapa_p": + self.name = "sky130_fd_bd_sram__sram_sp_wlstrapa_p" else: debug.error("Invalid version", -1) - design.design.__init__(self, name=self.name) - (self.width, self.height) = utils.get_libcell_size(self.name, - GDS["unit"], - layer["mem"]) - # pin_map = utils.get_libcell_pins(pin_names, self.name, GDS["unit"]) + design.__init__(self, name=self.name) + (self.width, self.height) = get_libcell_size(self.name, + GDS["unit"], + layer["mem"]) + # pin_map = get_libcell_pins(pin_names, self.name, GDS["unit"]) diff --git a/technology/sky130/modules/sky130_replica_bitcell.py b/technology/sky130/custom/sky130_replica_bitcell.py similarity index 90% rename from technology/sky130/modules/sky130_replica_bitcell.py rename to technology/sky130/custom/sky130_replica_bitcell.py index 8be40adc..2b30fb7a 100644 --- a/technology/sky130/modules/sky130_replica_bitcell.py +++ b/technology/sky130/custom/sky130_replica_bitcell.py @@ -6,13 +6,13 @@ # import debug -import bitcell_base +from modules import bitcell_base +from base import logical_effort from tech import parameter, drc from tech import cell_properties as props -import logical_effort -class sky130_replica_bitcell(bitcell_base.bitcell_base): +class sky130_replica_bitcell(bitcell_base): """ A single bit cell (6T, 8T, etc.) This module implements the single memory cell used in the design. It @@ -32,7 +32,7 @@ class sky130_replica_bitcell(bitcell_base.bitcell_base): size = 0.5 # This accounts for bitline being drained thought the access TX and internal node cin = 3 # Assumes always a minimum sizes inverter. Could be specified in the tech.py file. read_port_load = 0.5 # min size NMOS gate load - return logical_effort.logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False) + return logical_effort('bitline', size, cin, load + read_port_load, parasitic_delay, False) def input_load(self): """Return the relative capacitance of the access transistor gates""" diff --git a/technology/sky130/modules/sky130_replica_bitcell_array.py b/technology/sky130/custom/sky130_replica_bitcell_array.py similarity index 79% rename from technology/sky130/modules/sky130_replica_bitcell_array.py rename to technology/sky130/custom/sky130_replica_bitcell_array.py index bc5d2036..c7b3c609 100644 --- a/technology/sky130/modules/sky130_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_replica_bitcell_array.py @@ -6,10 +6,10 @@ # import debug -from replica_bitcell_array import replica_bitcell_array -from vector import vector -from sky130_bitcell_base_array import sky130_bitcell_base_array -from utils import round_to_grid +from modules import replica_bitcell_array +from base import vector +from .sky130_bitcell_base_array import sky130_bitcell_base_array +from base import round_to_grid from math import sqrt from tech import drc from tech import array_row_multiple @@ -99,8 +99,6 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar def add_pins(self): super().add_pins() - self.add_pin("vpb", "BIAS") - self.add_pin("vnb", "BIAS") def add_replica_columns(self): """ Add replica columns on left and right of array """ @@ -284,7 +282,7 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar # start_layer=pin.layer) min_area = drc["minarea_{}".format('m3')] - for track,supply, offset in zip(range(1,5),['vdd','vpb','vnb','gnd'],[min_area * 6,min_area * 6, 0, 0]): + for track,supply, offset in zip(range(1,5),['vdd','vdd','gnd','gnd'],[min_area * 6,min_area * 6, 0, 0]): y_offset = track * (pin_height + drc_width*2) self.add_segment_center('m2', vector(0,-y_offset), vector(self.width, -y_offset), drc["minwidth_{}".format('m2')]) self.add_segment_center('m2', vector(0,self.height + y_offset), vector(self.width, self.height + y_offset), drc["minwidth_{}".format('m2')]) @@ -323,18 +321,102 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar if len(self.rbls) > 0: for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): pin_names = self.replica_columns[self.rbls[0]].all_bitline_names + mirror = self.replica_col_insts[0].mirror for (bl_name, pin_name) in zip(names, pin_names): pin = inst.get_pin(pin_name) if 'rbl_bl' in bl_name: + # if mirror != "MY": + # bl_name = bl_name.replace("rbl_bl","rbl_br") self.add_layout_pin(text=bl_name, layer=pin.layer, offset=pin.ll().scale(1, 0), width=pin.width(), height=self.height) elif 'rbl_br' in bl_name: + # if mirror != "MY": + # bl_name = bl_name.replace("rbl_br","rbl_bl") self.add_layout_pin(text=bl_name, layer=pin.layer, offset=pin.ll().scale(1, 0) + vector(0,(pin_height + drc_width*2)), width=pin.width(), height=self.height - 2 *(pin_height + drc_width*2)) return + + def add_wordline_pins(self): + + # Wordlines to ground + self.gnd_wordline_names = [] + + for port in self.all_ports: + for bit in self.all_ports: + self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) + if bit != port: + self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit)) + + self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl] + + self.wordline_names = self.bitcell_array.wordline_names + self.all_wordline_names = self.bitcell_array.all_wordline_names + + # All wordlines including dummy and RBL + self.replica_array_wordline_names = [] + #self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) + for bit in range(self.rbl[0]): + self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]]) + self.replica_array_wordline_names.extend(self.all_wordline_names) + for bit in range(self.rbl[1]): + self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]]) + #self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) + + for port in range(self.rbl[0]): + self.add_pin(self.rbl_wordline_names[port][port], "INPUT") + self.add_pin_list(self.all_wordline_names, "INPUT") + for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): + self.add_pin(self.rbl_wordline_names[port][port], "INPUT") + + def create_instances(self): + """ Create the module instances used in this design """ + self.supplies = ["vdd", "gnd"] + + # Used for names/dimensions only + # self.cell = factory.create(module_type=OPTS.bitcell) + + # Main array + self.bitcell_array_inst=self.add_inst(name="bitcell_array", + mod=self.bitcell_array) + self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies) + # Replica columns + self.replica_col_insts = [] + for port in self.all_ports: + if port in self.rbls: + self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), + mod=self.replica_columns[port])) + self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies + ["gnd"] + ["gnd"]) + else: + self.replica_col_insts.append(None) + + # Dummy rows under the bitcell array (connected with with the replica cell wl) + self.dummy_row_replica_insts = [] + # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! + for port in self.all_ports: + self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), + mod=self.dummy_row)) + self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies) + + # Top/bottom dummy rows or col caps + self.dummy_row_insts = [] + self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", + mod=self.col_cap_bottom)) + self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"]) + self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", + mod=self.col_cap_top)) + self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"]) + + # Left/right Dummy columns + self.dummy_col_insts = [] + self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", + mod=self.row_cap_left)) + self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies) + self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", + mod=self.row_cap_right)) + self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies) diff --git a/technology/sky130/modules/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py similarity index 84% rename from technology/sky130/modules/sky130_replica_column.py rename to technology/sky130/custom/sky130_replica_column.py index a900b0fe..8997d4f2 100644 --- a/technology/sky130/modules/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -6,10 +6,10 @@ # import debug -from sky130_bitcell_base_array import sky130_bitcell_base_array +from .sky130_bitcell_base_array import sky130_bitcell_base_array from sram_factory import factory from globals import OPTS -import geometry +from base import geometry from tech import layer @@ -81,39 +81,35 @@ class sky130_replica_column(sky130_bitcell_base_array): def add_pins(self): self.create_all_bitline_names() - self.create_all_wordline_names(self.row_size+2) + #self.create_all_wordline_names(self.row_size+2) # +2 to add fake wl pins for colends - + self.create_all_wordline_names(self.row_size+1, 1) self.add_pin_list(self.all_bitline_names, "OUTPUT") self.add_pin_list(self.all_wordline_names, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") + self.add_pin("top_gate", "INPUT") + self.add_pin("bot_gate", "INPUT") + def add_modules(self): self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1") - self.add_mod(self.replica_cell) self.cell = self.replica_cell self.replica_cell2 = factory.create(module_type="replica_bitcell_1port", version="opt1a") - self.add_mod(self.replica_cell2) self.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1") self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1") self.strap1 = factory.create(module_type="internal", version="wlstrap") - self.add_mod(self.strap1) self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.add_mod(self.strap2) + self.strap3 = factory.create(module_type="internal", version="wlstrapa_p") self.colend = factory.create(module_type="col_cap", version="colend") self.edge_cell = self.colend - self.add_mod(self.colend) self.colenda = factory.create(module_type="col_cap", version="colenda") - self.add_mod(self.colenda) self.colend_p_cent = factory.create(module_type="col_cap", version="colend_p_cent") - self.add_mod(self.colend_p_cent) self.colenda_p_cent = factory.create(module_type="col_cap", version="colenda_p_cent") - self.add_mod(self.colenda_p_cent) def create_instances(self): self.cell_inst = {} @@ -132,33 +128,33 @@ class sky130_replica_column(sky130_bitcell_base_array): self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) self.connect_inst(self.get_bitcell_pins(row, 0)) row_layout.append(self.strap2) - self.add_inst(name=name + "_strap", mod=self.strap2) - self.connect_inst(self.get_strap_pins(row, 0)) + self.add_inst(name=name + "_strap_p", mod=self.strap2) + self.connect_inst(self.get_strap_pins(row, 0, name + "_strap_p")) alternate_bitcell = 1 else: row_layout.append(self.replica_cell2) self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell2) self.connect_inst(self.get_bitcell_pins(row, 0)) - row_layout.append(self.strap2) - self.add_inst(name=name + "_strap", mod=self.strap2) + row_layout.append(self.strap3) + self.add_inst(name=name + "_strap", mod=self.strap3) self.connect_inst(self.get_strap_pins(row, 0)) alternate_bitcell = 0 elif (row == 0): row_layout.append(self.colend) self.cell_inst[row]=self.add_inst(name=name, mod=self.colend) - self.connect_inst(self.get_col_cap_p_pins(row, 0)) + self.connect_inst(self.get_col_cap_pins(row, 0)) row_layout.append(self.colend_p_cent) self.add_inst(name=name + "_cap", mod=self.colend_p_cent) - self.connect_inst(self.get_col_cap_pins(row, 0)) + self.connect_inst(self.get_col_cap_p_pins(row, 0)) elif (row == self.total_size - 1): row_layout.append(self.colenda) self.cell_inst[row]=self.add_inst(name=name, mod=self.colenda) - self.connect_inst(self.get_col_cap_p_pins(row, 0)) + self.connect_inst(self.get_col_cap_pins(row, 0)) row_layout.append(self.colenda_p_cent) self.add_inst(name=name + "_cap", mod=self.colenda_p_cent) - self.connect_inst(self.get_col_cap_pins(row, 0)) + self.connect_inst(self.get_col_cap_p_pins(row, 0)) self.array_layout.append(row_layout) @@ -218,7 +214,7 @@ class sky130_replica_column(sky130_bitcell_base_array): for port in self.all_ports: for row in range(row_range_min, row_range_max): wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port)) - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), + self.add_layout_pin(text="wl_{0}_{1}".format(port, row_range_max-row), layer=wl_pin.layer, offset=wl_pin.ll().scale(0, 1), width=self.width, @@ -229,28 +225,32 @@ class sky130_replica_column(sky130_bitcell_base_array): # add only 1 label per col for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) - if row == 2: - if 'VPB' in self.cell_inst[row].mod.pins: - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + #if row == 2: + if 'VPB' or 'vpb' in self.cell_inst[row].mod.pins: + pin = inst.get_pin("vpb") + self.objs.append(geometry.rectangle(layer["nwell"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - if 'VNB' in self.cell_inst[row].mod.pins: - try: - from tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - except: + if 'VNB' or 'vnb' in self.cell_inst[row].mod.pins: + print("welling") + try: + from tech import layer_override + if layer_override['VNB']: pin = inst.get_pin("vnb") - self.add_label("vdd", pin.layer, pin.center()) + self.add_label("gnd", pin.layer, pin.center()) + self.objs.append(geometry.rectangle(layer["pwellp"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + + + except: + pin = inst.get_pin("vnb") + self.add_label("gnd", pin.layer, pin.center()) def exclude_all_but_replica(self): """ diff --git a/technology/sky130/modules/sky130_row_cap.py b/technology/sky130/custom/sky130_row_cap.py similarity index 93% rename from technology/sky130/modules/sky130_row_cap.py rename to technology/sky130/custom/sky130_row_cap.py index bdd0aa95..1c81b8dd 100644 --- a/technology/sky130/modules/sky130_row_cap.py +++ b/technology/sky130/custom/sky130_row_cap.py @@ -6,11 +6,11 @@ # import debug -import design +from base import design from tech import cell_properties as props -class sky130_row_cap(design.design): +class sky130_row_cap(design): def __init__(self, version, name=""): diff --git a/technology/sky130/modules/sky130_row_cap_array.py b/technology/sky130/custom/sky130_row_cap_array.py similarity index 93% rename from technology/sky130/modules/sky130_row_cap_array.py rename to technology/sky130/custom/sky130_row_cap_array.py index a82f5558..45b63c77 100644 --- a/technology/sky130/modules/sky130_row_cap_array.py +++ b/technology/sky130/custom/sky130_row_cap_array.py @@ -6,7 +6,7 @@ # from sram_factory import factory -from sky130_bitcell_base_array import sky130_bitcell_base_array +from .sky130_bitcell_base_array import sky130_bitcell_base_array from globals import OPTS @@ -51,24 +51,16 @@ class sky130_row_cap_array(sky130_bitcell_base_array): """ Add the modules used in this design """ if self.column_offset == 0: self.top_corner = factory.create(module_type="corner", location="ul") - self.add_mod(self.top_corner) self.bottom_corner =factory.create(module_type="corner", location="ll") - self.add_mod(self.bottom_corner) self.rowend1 = factory.create(module_type="row_cap", version="rowend_replica") - self.add_mod(self.rowend1) self.rowend2 = factory.create(module_type="row_cap", version="rowenda_replica") - self.add_mod(self.rowend2) else: self.top_corner = factory.create(module_type="corner", location="ur") - self.add_mod(self.top_corner) self.bottom_corner = factory.create(module_type="corner", location="lr") - self.add_mod(self.bottom_corner) self.rowend1 = factory.create(module_type="row_cap", version="rowend") - self.add_mod(self.rowend1) self.rowend2 = factory.create(module_type="row_cap", version="rowenda") - self.add_mod(self.rowend2) self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") diff --git a/technology/sky130/tech/sky130.lydrc b/technology/sky130/tech/sky130.lydrc new file mode 100644 index 00000000..3c569c12 --- /dev/null +++ b/technology/sky130/tech/sky130.lydrc @@ -0,0 +1,922 @@ + + + + + drc + + + + false + false + + true + drc_scripts + tools_menu.drc.end + dsl + drc-dsl-xml + # +# DRC for SKY130 according to : +# https://skywater-pdk.readthedocs.io/en/latest/rules/periphery.html +# https://skywater-pdk.readthedocs.io/en/latest/rules/layers.html +# +# Distributed under GNU GPLv3: https://www.gnu.org/licenses/ +# +# History : +# 2020-10-04 : v1.0 : initial release +# +########################################################################################## + +# optionnal for a batch launch : klayout -b r drc_sky130.lydrc -rd input=my_layout.gds -rd topcell=your_topcell -rd output=drc_sky130.lyrdb +if $input + if $topcell + source($input,$topcell) + else + source($input) + end +end + +if $output + report("SKY130 DRC runset", $output) +else + report("SKY130 DRC runset", File.join(File.dirname(RBA::CellView::active.filename), "sky130_drc.txt")) +end + +AL = true # do not change +CU = false # do not change +# choose betwen only one of AL or CU back-end flow here : +backend_flow = AL + +# enable / disable rule groups +FEOL = true # front-end-of-line checks +BEOL = true # back-end-of-line checks +OFFGRID = true # manufacturing grid/angle checks + +# klayout setup +######################## +# use a tile size of 1mm - not used in deep mode- +tiles(1000.um) +# use a tile border of 10 micron: +tile_borders(1.um) +#no_borders + +# hierachical +deep + +# use 4 cpu cores +threads(4) +# if more inof is needed, set true +verbose(true) + +# layers definitions +######################## + +# all except purpose (datatype) 5 -- label and 44 -- via +li_wildcard = "67/0-4,6-43,45-*" +mcon_wildcard = "67/44" + +m1_wildcard = "68/0-4,6-43,45-*" +via_wildcard = "68/44" + +m2_wildcard = "69/0-4,6-43,45-*" +via2_wildcard = "69/44" + +m3_wildcard = "70/0-4,6-43,45-*" +via3_wildcard = "70/44" + +m4_wildcard = "71/0-4,6-43,45-*" +via4_wildcard = "71/44" + +m5_wildcard = "72/0-4,6-43,45-*" + +diff = input(65, 20) +tap = polygons(65, 44) +nwell = polygons(64, 20) +dnwell = polygons(64, 18) +pwbm = polygons(19, 44) +pwde = polygons(124, 20) +natfet = polygons(124, 21) +hvtr = polygons(18, 20) +hvtp = polygons(78, 44) +ldntm = polygons(11, 44) +hvi = polygons(75, 20) +tunm = polygons(80, 20) +lvtn = polygons(125, 44) +poly = polygons(66, 20) +hvntm = polygons(125, 20) +nsdm = polygons(93, 44) +psdm = polygons(94, 20) +rpm = polygons(86, 20) +urpm = polygons(79, 20) +npc = polygons(95, 20) +licon = polygons(66, 44) + +li = polygons(li_wildcard) +mcon = polygons(mcon_wildcard) + +m1 = polygons(m1_wildcard) +via = polygons(via_wildcard) + +m2 = polygons(m2_wildcard) +via2 = polygons(via2_wildcard) + +m3 = polygons(m3_wildcard) +via3 = polygons(via3_wildcard) + +m4 = polygons(m4_wildcard) +via4 = polygons(via4_wildcard) + +m5 = polygons(m5_wildcard) + +pad = polygons(76, 20) +nsm = polygons(61, 20) +capm = polygons(89, 44) +cap2m = polygons(97, 44) +vhvi = polygons(74, 21) +uhvi = polygons(74, 22) +npn = polygons(82, 20) +inductor = polygons(82, 24) +vpp = polygons(82, 64) +pnp = polygons(82, 44) +lvs_prune = polygons(84, 44) +ncm = polygons(92, 44) +padcenter = polygons(81, 20) +mf = polygons(76, 44) +areaid_sl = polygons(81, 1) +areaid_ce = polygons(81, 2) +areaid_fe = polygons(81, 3) +areaid_sc = polygons(81, 4) +areaid_sf = polygons(81, 6) +areaid_sw = polygons(81, 7) +areaid_sr = polygons(81, 8) +areaid_mt = polygons(81, 10) +areaid_dt = polygons(81, 11) +areaid_ft = polygons(81, 12) +areaid_ww = polygons(81, 13) +areaid_ld = polygons(81, 14) +areaid_ns = polygons(81, 15) +areaid_ij = polygons(81, 17) +areaid_zr = polygons(81, 18) +areaid_ed = polygons(81, 19) +areaid_de = polygons(81, 23) +areaid_rd = polygons(81, 24) +areaid_dn = polygons(81, 50) +areaid_cr = polygons(81, 51) +areaid_cd = polygons(81, 52) +areaid_st = polygons(81, 53) +areaid_op = polygons(81, 54) +areaid_en = polygons(81, 57) +areaid_en20 = polygons(81, 58) +areaid_le = polygons(81, 60) +areaid_hl = polygons(81, 63) +areaid_sd = polygons(81, 70) +areaid_po = polygons(81, 81) +areaid_it = polygons(81, 84) +areaid_et = polygons(81, 101) +areaid_lvt = polygons(81, 108) +areaid_re = polygons(81, 125) +areaid_ag = polygons(81, 79) +poly_rs = polygons(66, 13) +diff_rs = polygons(65, 13) +pwell_rs = polygons(64, 13) +li_rs = polygons(67, 13) +cfom = polygons(22, 20) + + +# Define a new custom function that selects polygons by their number of holes: +# It will return a new layer containing those polygons with min to max holes. +# max can be nil to omit the upper limit. +class DRC::DRCLayer + def with_holes(min, max) + new_data = RBA::Region::new + self.data.each do |p| + if p.holes >= (min || 0) && (!max || p.holes <= max) + new_data.insert(p) + end + end + DRC::DRCLayer::new(@engine, new_data) + end +end + +# DRC section +######################## +info("DRC section") + +if FEOL +info("FEOL section") +gate = diff & poly + +# dnwell +dnwell.width(3.0, euclidian).output("dnwell.2", "dnwell.2 : min. dnwell width : 3.0um") +dnwell.not(uhvi).not(areaid_po).isolated(6.3, euclidian).output("dnwell.3", "dnwell.3 : min. dnwell spacing : 6.3um") +dnwell.and(pnp).output("dnwell.4", "dnwell.4 : dnwell must not overlap pnp") +dnwell.and(psdm).edges.not(psdm.edges).output("dnwell.5", "p+ must not straddle dnwell") +# dnwell.6 rue not coded + +# nwell +nwell.width(0.84, euclidian).output("nwell.1", "nwell.1 : min. nwell width : 0.84um") +nwell.isolated(1.27, euclidian).output("nwell.2a", "nwell.2a : min. nwell spacing (merged if less) : 1.27um") +# rule nwell.4 is suitable for digital cells +#nwell.not(uhvi).not(areaid_en20).not_interacting(tap.and(licon).and(li)).output("nwell.4", "nwell4 : all nwell exempt inside uhvi must contain a n+tap") +nwell.enclosing(dnwell.not(uhvi).not(areaid_po), 0.4, euclidian).output("nwell.5", "nwell.5 : min. nwell enclosing dnwell exempt unside uhvi : 0.4um") +dnwell.enclosing(nwell.not(uhvi), 1.03, euclidian).output("nwell.6", "nwell.6 : min. dnwell enclosing nwell exempt unside uhvi : 1.03um") +dnwell.separation(nwell, 4.5, euclidian).output("nwell.7", "nwell.7 : min. dnwell separation nwell : 4.5um") + +# pwbm +pwbm.not(uhvi).output("pwbm", "pwbm must be inside uhvi") +dnwell.and(uhvi).edges.not(pwbm).output("pwbm.4", "pwbm.4 : dnwell inside uhvi must be enclosed by pwbm") + +# pwde +pwde.not(pwbm).output("pwdem.3", "pwdem.3 : pwde must be inside pwbm") +pwde.not(uhvi).output("pwdem.4", "pwdem.4 : pwde must be inside uhvi") +pwde.not(dnwell).output("pwdem.5", "pwdem.5 : pwde must be inside dnwell") + +# hvtp +#hvtp.not(nwell).output("hvtp", "hvtp must inside nwell") +hvtp.width(0.38, euclidian).output("hvtp.1", "hvtp.1 : min. hvtp width : 0.38um") +hvtp.isolated(0.38, euclidian).output("hvtp.2", "hvtp.2 : min. hvtp spacing : 0.38um") +hvtp.enclosing(gate.and(psdm), 0.18, euclidian).output("hvtp.3", "hvtp.3 : min. hvtp enclosure of pfet gate : 0.18um") +hvtp.separation(gate.and(psdm), 0.18, euclidian).output("hvtp.4", "hvtp.4 : min. hvtp spacing pfet gate: 0.18um") +hvtp.with_area(0..0.265).output("hvtp.5", "hvtp.5 : min. hvtp area : 0.265um²") + +# hvtr +hvtr.width(0.38, euclidian).output("hvtr.1", "hvtr.1 : min. hvtr width : 0.38um") +hvtr.isolated(0.38, euclidian).output("hvtr.2", "hvtr.2 : min. hvtr spacing : 0.38um") + +# lvtn +lvtn.width(0.38, euclidian).output("lvtn.1", "lvtn.1 : min. lvtn width : 0.38um") +lvtn.isolated(0.38, euclidian).output("lvtn.2", "lvtn.2 : min. lvtn spacing : 0.38um") +lvtn.separation(diff.and(poly).not(uhvi), 0.18, euclidian).output("lvtn.3a", "lvtn.3a : min. lvtn spacing to gate : 0.18um") +lvtn.separation(diff.and(nwell).not(poly), 0.235, projection).output("lvtn.3b", "lvtn.3b : min. lvtn spacing to pfet s/d : 0.18um") +lvtn.enclosing(gate.not(uhvi), 0.18, euclidian).output("lvtn.4b", "lvtn.4b : min. lvtn enclosing to gate : 0.18um") +lvtn.separation(hvtp, 0.38, euclidian).output("lvtn.9", "lvtn.9 : min. lvtn spacing hvtp : 0.38um") +nwell.not_interacting(gate.and(nwell.not(hvi).not(areaid_ce))).enclosing(lvtn.not(uhvi), 0.18, euclidian).polygons.without_area(0).output("lvtn.4b", "lvtn.4b : min. lvtn enclosure of gate : 0.18um") +lvtn.separation(nwell.inside(areaid_ce), 0.38, euclidian).output("lvtn.12", "lvtn.12 : min. lvtn spacing nwell inside areaid.ce : 0.38um") +lvtn.with_area(0..0.265).output("lvtn.13", "lvtn.13 : min. lvtn area : 0.265um²") + +# ncm +ncm.and(tap.and(nwell).or(diff.not(nwell))).output("ncm.x.3", "ncm.x.3 : ncm must not overlap n+diff") +ncm.width(0.38, euclidian).output("ncm.1", "ncm.1 : min. ncm width : 0.38um") +ncm.isolated(0.38, euclidian).output("ncm.2", "ncm.2 : min. ncm spacing manual merge if smaller : 0.38um") +ncm.enclosing(diff.and(nwell), 0.18, euclidian).output("ncm.3", "ncm.3 : min. ncm enclosure of p+diff : 0.18um") +ncm.separation(lvtn.and(diff), 0.23, euclidian).output("ncm.5", "ncm.5 : min. ncm spacing lvtn diff : 0.23um") +ncm.separation(diff.not(nwell), 0.2, euclidian).output("ncm.6", "ncm.6 : min. ncm spacing nfet : 0.2um") +ncm.with_area(0..0.265).output("ncm.7", "ncm.13 : min. ncm area : 0.265um²") + +# diff-tap +difftap = diff + tap +difftap.width(0.15, euclidian).output("difftap.1", "difftap.1 : min. difftap width : 0.15um") +not_in_cell1 = layout(source.cell_obj).select("s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b" , "-s8fpls_pl8", "-s8fpls_rdrv4" , "-s8fpls_rdrv4f", "-s8fpls_rdrv8") +not_in_cell1_diff = not_in_cell1.input(65, 20) +not_in_cell1_diff.not(areaid_sc).not(poly).edges.and(gate.edges).with_length(0,0.42).output("difftap.2", "difftap.2: min. gate (exempt areaid.sc) width : 0.42um") +diff.and(areaid_sc).not(poly).edges.and(gate.edges).with_length(0,0.36).output("difftap.2", "difftap.2: min. gate inside areaid.sc width : 0.36um") +difftap.isolated(0.27, euclidian).output("difftap.3", "difftap.3 : min. difftap spacing : 0.27um") +tap.edges.and(diff.edges).with_length(0,0.29).output("difftap.4", "difftap.4 : min. tap bound by diffusion : 0.29um") +tap.edges.and(diff.edges).space(0.4, projection).output("difftap.5", "difftap.5 : min. tap bound by 2 diffusions : 0.4um") +(tap.edges.and(diff.edges)).extended(0.01, 0.01, 0, 0, false).not(tap.and(diff)).and(diff.or(tap)).output("difftap.6", "difftap.6 : diff and tap not allowed to extend beyong their abutting ege") +tap.edges.not_interacting(diff.edges).separation(diff.edges, 0.13, euclidian).output("difftap.7", "difftap.7 : min. diff/tap spacing to non-coincident diff edge : 0.13um") +diff.edges.not_interacting(tap.edges).separation(tap.edges, 0.13, euclidian).output("difftap.7", "difftap.7 : min. diff/tap spacing to non-coincident tap edge : 0.13um") +nwell.enclosing(diff.not(uhvi).and(psdm), 0.18, euclidian).output("difftap.8", "difftap.8 : min. p+diff enclosure by nwell : 0.18um") +diff.not(uhvi).and(nsdm).separation(nwell, 0.34, euclidian).output("difftap.9", "difftap.9 : min. n+diff spacing to nwell : 0.34um") +nwell.enclosing(tap.not(uhvi).and(nsdm), 0.18, euclidian).output("difftap.10", "difftap.10 : min. n+tap enclosure by nwell : 0.18um") +tap.not(uhvi).and(psdm).separation(nwell, 0.13, euclidian).output("difftap.11", "difftap.11 : min. p+tap spacing to nwell : 0.13um") + +# tunm +tunm.width(0.41, euclidian).output("tunm.1", "tunm.1 : min. tunm width : 0.41um") +tunm.isolated(0.5, euclidian).output("tunm.2", "tunm.2 : min. tunm spacing : 0.5um") +tunm.enclosing(gate, 0.095, euclidian).output("tunm.3", "tunm.3 : min. tunm beyond gate : 0.095um") +tunm.separation(gate.not_interacting(tunm), 0.095, euclidian).output("tunm.4", "tunm.4 : min. tunm spacing to gate outside tunm: 0.095um") +gate.and(tunm).edges.not(gate.edges).output("tunm.5", "tunm.5 : gate must not straddle tunm") +tunm.not(dnwell).output("tunm.6a", "tunm.6a : tunm not allowed outside dnwell") +tunm.with_area(0..0.672).output("tunm.7", "tunm.7 : min. tunm area : 0.672um²") + +# poly +poly.width(0.15, euclidian).output("poly.1a", "poly.1a : min. poly width : 0.15um") +poly.not(diff).edges.and(gate.and(lvtn).edges).space(0.35, euclidian).output("poly.1b", "poly.1b: min. lvtn gate width : 0.35um") +poly.isolated(0.21, euclidian).output("poly.2", "poly.2 : min. poly spacing : 0.21um") +poly.and(rpm.or(urpm).or(poly_rs)).width(0.33, euclidian).output("poly.3", "poly.3 : min. poly resistor width : 0.33um") +poly.not(gate).separation(diff, 0.075, projection).polygons.without_area(0).output("poly.4", "poly.4 : min. poly on field spacing to diff : 0.075um") +poly.not(gate).separation(tap, 0.055, euclidian).output("poly.5", "poly.5 : min. poly on field spacing to tap : 0.055um") +gate.separation(tap, 0.3, projection).polygons.and(diff).output("poly.6", "poly.6 : min. gate spacing to tap : 0.3um") +diff.enclosing(gate, 0.25, projection).polygons.without_area(0).output("poly.7", "poly.7 : min. source/drain length : 0.25um") +poly.enclosing(gate, 0.13, projection).polygons.without_area(0).output("poly.8", "poly.8 : min. poly extention gate (endcap) : 0.13um") +poly.and(rpm.or(urpm).or(poly_rs)).separation(poly.or(difftap), 0.48, euclidian).polygons.without_area(0).output("poly.9", "poly.9 : min. poly resistor space to poly or diff/tap : 0.48um") +diff.merged.edges.end_segments(0.01).and(poly).output("poly.10", "poly.10 : poly must not overlap diff corner") +gate.with_angle(0 .. 90).output("poly.11", "poly.11 : non 90 degree angle gate") +not_in_cell3 = layout(source.cell_obj).select("s8fgvr_n_fg2") +not_in_cell3_poly = not_in_cell3.input(66, 20) +not_in_cell3_poly.not(hvi).not(nwell.not(hvi)).and(tap).output("poly.12", "poly.12 : poly must not overlap tap") +poly.and(diff_rs).output("poly.15", "poly.15 : poly must not overlap diff resistor") + +# rpm +rpm.width(1.27, euclidian).output("rpm.1a", "rpm.1a : min. rpm width : 1.27um") +rpm.isolated(0.84, euclidian).output("rpm.2", "rpm.2 : min. rpm spacing : 0.84um") +rpm.enclosing(poly.and(poly_rs).and(psdm), 0.2, euclidian).output("rpm.3", "rpm.3 : min. rpm enclosure of poly resistor : 0.2um") +psdm.enclosing(poly.and(poly_rs).and(rpm), 0.11, euclidian).output("rpm.4", "rpm.4 : min. psdm enclosure of poly resistor : 0.11um") +npc.enclosing(poly.and(poly_rs).and(rpm), 0.095, euclidian).output("rpm.5", "rpm.5 : min. npc enclosure of poly resistor : 0.095um") +rpm.separation(nsdm, 0.2, euclidian).output("rpm.6", "rpm.6 : min. rpm spacing nsdm: 0.2um") +rpm.separation(poly, 0.2, euclidian).output("rpm.7", "rpm.7 : min. rpm spacing poly: 0.2um") +rpm.and(poly).edges.not(poly.edges).output("rpm.8", "rpm.8 : poly must not straddle rpm") +poly.and(poly_rs).and(rpm).separation(hvntm, 0.185, euclidian).output("rpm.9", "rpm.9 : min. poly resistor spacing hvntm: 0.185um") +rpm.and(pwbm).output("rpm.10", "rpm.107 : min. rpm spacing pwbm: na") + +# varac +varac = poly & tap & (nwell - hvi) - areaid_ce +tap.not(poly).edges.and(varac.edges).space(0.18, euclidian).output("varac.1", "varac.1: min. varac channel length : 0.18um") +tap.and(poly).edges.and(varac.edges).space(1.0, euclidian).output("varac.2", "varac.2: min. varac channel wdth : 1.0um") +varac.separation(hvtp, 0.18, euclidian).output("varac.3", "varac.3: min. varac channel space to hvtp : 0.18um") +varac.separation(licon.and(tap), 0.25, euclidian).output("varac.4", "varac.4: min. varac channel space to licon on tap : 0.25um") +nwell.enclosing(poly.overlapping(varac), 0.15, euclidian).output("varac.5", "varac.5: min. nwell enclosure of poly overlapping varac channel : 0.15um") +tap.overlapping(varac).separation(difftap, 0.27, euclidian).polygons.without_area(0).output("varac.6", "varac.6: min. varac channel tap space to difftap : 0.27um") +nwell.overlapping(varac).and(diff.and(nwell)).output("varac.7", "varac.7: nwell overlapping varac channel must not overlap p+diff") + +# photo +photodiode = dnwell & areaid_po +photodiode.edges.without_length(3.0).output("photo.2", "photo.2 : minimum/maximum width of photodiode : 3.0um") +photodiode.isolated(5.0, euclidian).output("photo.3", "photo.3 : mini. photodiode spacing : 5.0um") +photodiode.separation(dnwell, 5.3, euclidian).output("photo.4", "photo.4 : mini. photodiode spacing to dnwell : 5.3um") +areaid_po.not(dnwell).output("photo.5.6", "photo.5.6 : photodiode edges must coincide areaid.po and enclosed by dnwell") +photodiode.not(tap.not(nwell).holes).output("photo.7", "photo.7 : photodiode must be enclosed by p+tap ring") +photodiode.and(nwell).edges.without_length(0.84).output("photo.8", "photo.8 : minimum/maximum width of nwell inside photodiode : 0.84um") +areaid_po.edges.and(photodiode.and(nwell).sized(1.08)).without_length(12.0).output("photo.9", "photo.9 : minimum/maximum enclosure of nwell by photodiode : 1.08um") +photodiode.and(tap).edges.without_length(0.41).output("photo.10", "photo.10 : minimum/maximum width of tap inside photodiode : 0.41um") + +# npc +npc.width(0.27, euclidian).output("npc.1", "npc.1 : min. npc width : 0.27um") +npc.isolated(0.27, euclidian).output("npc.2", "npc.2 : min. npc spacing, should be mnually merge if less : 0.27um") +npc.separation(gate, 0.09, euclidian).output("npc.4", "npc.4 : min. npc spacing to gate : 0.09um") + +# nsdm/psdm +npsdm = nsdm + psdm +nsdm.width(0.38, euclidian).output("nsdm.1", "nsdm.1 : min. nsdm width : 0.38um") +psdm.width(0.38, euclidian).output("psdm.1", "psdm.1 : min. psdm width : 0.38um") +nsdm.isolated(0.38, euclidian).output("n/psdm.1", "n/psdm.1 : min. nsdm spacing, should be mnually merge if less : 0.38um") +psdm.isolated(0.38, euclidian).output("n/psdm.1", "n/psdm.1 : min. psdm spacing, should be mnually merge if less : 0.38um") +npsdm.enclosing(diff, 0.125, euclidian).polygons.not(tap.sized(0.125)).output("n/psdm.5a", "n/psdm.5a : min. n/psdm enclosure diff except butting edge : 0.125um") +npsdm.enclosing(tap, 0.125, euclidian).polygons.not(diff.sized(0.125)).output("n/psdm.5b", "n/psdm.5b : min. n/psdm enclosure tap except butting edge : 0.125um") +tap.edges.and(diff.edges).not(npsdm).output("n/psdm.6", "n/psdm.6 : min. n/psdm enclosure of butting edge : 0.0um") +nsdm.and(difftap).separation(psdm.and(difftap), 0.13, euclidian).polygons.without_area(0).output("n/psdm.7", "n/psdm.7 : min. nsdm diff spacing to psdm diff except butting edge : 0.13um") +diff.and((nsdm.and(nwell)).or(psdm.not(nwell))).output("n/psdm.8", "n/psdm.8 : diff should be the opposite type of well/substrate underneath") +tap.and((nsdm.not(nwell)).or(psdm.and(nwell))).output("n/psdm.8", "n/psdm.8 : tap should be the same type of well/substrate underneath") +tap.and(diff).without_area(0).output("tap and diff", "tap and diff must not overlap") +nsdm.with_area(0..0.265).output("n/psdm.10a", "n/psdm.10a : min. nsdm area : 0.265um²") +psdm.with_area(0..0.265).output("n/psdm.10b", "n/psdm.10b : min. psdm area : 0.265um²") + +# licon +licon.not(poly.interacting(poly_rs).and(rpm)).edges.without_length(0.17).output("licon.1", "licon.1 : minimum/maximum width of licon : 0.17um") +licon.and(poly.interacting(poly_rs).and(rpm)).not_interacting((licon.and(poly.interacting(poly_rs).and(rpm)).edges.with_length(0.19)).or(licon.and(poly.interacting(poly_rs).and(rpm)).edges.with_length(2.0))).output("licon.1b/c", "licon.1b/c : minimum/maximum width/length of licon inside poly resistor : 2.0/0.19um") +licon.isolated(0.17, euclidian).output("licon.2", "licon.2 : min. licon spacing : 0.17um") +licon.and(poly.interacting(poly_rs).and(rpm)).edges.with_length(0.19).space(0.35, euclidian).output("licon.2b", "licon.2b : min. licon 0.19um edge on resistor spacing : 0.35um") +licon.interacting(licon.and(poly.interacting(poly_rs).and(rpm)).edges.with_length(2.0)).separation(licon.and(poly.interacting(poly_rs).and(rpm)), 0.51, euclidian).output("licon.2c", "licon.2c : min. licon 2.0um edge on resistor spacing : 0.51um") +licon.and(poly.interacting(poly_rs).and(rpm)).separation(licon.not(poly.interacting(poly_rs).and(rpm)), 0.51, euclidian).output("licon.2d", "licon.2d : min. licon on resistor spacing other licon : 0.51um") +# rule licon.3 not coded +licon.not(li).not(poly.or(diff).or(tap)).output("licon.4", "licon.4 : min. licon must overlap li and (poly or tap or diff) ") +diff.enclosing(licon, 0.04, euclidian).output("licon.5", "licon.5 : min. diff enclosure of licon : 0.04um") +tap.edges.and(diff.edges).separation(licon.and(tap).edges, 0.06, euclidian).output("licon.6", "licon.6 : min. abutting edge spacing to licon tap : 0.06um") +licon_edges_with_less_enclosure_tap = tap.enclosing(licon, 0.12, projection).second_edges +opposite1 = (licon.edges - licon_edges_with_less_enclosure_tap).width(0.17 + 1.dbu, projection).polygons +licon.not_interacting(opposite1).output("licon.7", "licon.7 : min. tap enclosure of licon by one of 2 opposite edges : 0.12um") +poly.enclosing(licon, 0.05, euclidian).output("licon.8", "licon.8 : min. poly enclosure of licon : 0.05um") +licon008 = licon.interacting(poly.enclosing(licon, 0.08, euclidian).polygons) +licon_edges_with_less_enclosure_poly = poly.enclosing(licon, 0.08, projection).second_edges +opposite2 = (licon.edges - licon_edges_with_less_enclosure_poly).width(0.17 + 1.dbu, projection).polygons +licon008.not_interacting(opposite2).output("licon.8a", "licon.8a : min. poly enclosure of licon by one of 2 opposite edges : 0.08um") +# rule licon.9 not coded +licon.and(tap.and(nwell.not(hvi))).separation(varac, 0.25, euclidian).output("licon.10", "licon.10 : min. licon spacing to varac channel : 0.25um") +not_in_cell4 = layout(source.cell_obj).select("-s8fs_gwdlvx4", "-s8fs_gwdlvx8", "-s8fs_hvrsw_x4", "-s8fs_hvrsw8", "-s8fs_hvrsw264", "-s8fs_hvrsw520", "-s8fs_rdecdrv", "-s8fs_rdec8”, “s8fs_rdec32", "-s8fs_rdec264", "-s8fs_rdec520") +not_in_cell4_licon = not_in_cell4.input(66, 44) +not_in_cell4_licon.and(diff.or(tap)).separation(gate.not(areaid_sc), 0.055, euclidian).output("licon.11", "licon.11 : min. licon spacing to gate : 0.055um") +licon.and(diff.or(tap)).separation(gate.and(areaid_sc), 0.05, euclidian).output("licon.11a", "licon.11a : min. licon spacing to gate inside areaid.sc : 0.05um") +in_cell4 = layout(source.cell_obj).select("+s8fs_gwdlvx4", "+s8fs_gwdlvx8", "+s8fs_hvrsw_x4", "+s8fs_hvrsw8", "+s8fs_hvrsw264", "+s8fs_hvrsw520") +in_cell4_licon = in_cell4.input(66, 44) +in_cell4_licon.and(diff.or(tap)).separation(gate, 0.04, euclidian).output("licon.11c", "licon.11c : min. licon spacing to gate for specific cells: 0.04um") +# rules 11.b , 11.d not coded +diff.interacting(gate).not(diff.interacting(gate).width(5.7, euclidian).polygons).output("licon.12", "licon.12 : max. sd width without licon : 5.7um") +licon.and(diff.or(tap)).separation(npc, 0.09, euclidian).output("licon.13", "licon.13 : min. difftap licon spacing to npc : 0.09um") +licon.and(poly).separation(diff.or(tap), 0.19, euclidian).output("licon.14", "licon.14 : min. poly licon spacing to difftap : 0.19um") +npc.enclosing(licon.and(poly), 0.1, euclidian).output("licon.15", "licon.15 : min. npc enclosure of poly-licon : 0.1um") +# rule licon.16 not applicable for the diff for the nmos of a nand gates or the pmos of a nor gates +#diff.not(gate).not_interacting(licon).output("licon.16", "licon.16 : diff must enclose one licon") +tap.not(uhvi).not_interacting(licon).output("licon.16", "licon.16 : tap must enclose one licon") +poly.and(tap).edges.not(tap.edges).output("licon.17", "licon.17 : tap must not straddle poly") +npc.not_interacting(licon.and(poly)).output("licon.18", "licon.18 : npc mut enclosed one poly-licon") + +# vpp +vpp.width(1.43, euclidian).output("vpp.1", "vpp.1 : min. vpp width : 1.43um") +# rules 1.b, 1.c not coded +vpp.and(poly.or(difftap)).output("vpp.3", "vpp.3 : vpp must not overlapp poly or diff or tap") +vpp.and(nwell).edges.not(vpp.edges).output("vpp.4", "vpp.4 : vpp must not straddle nwell") +vpp.and(dnwell).edges.not(vpp.edges).output("vpp.4", "vpp.4 : vpp must not straddle dnwell") +vpp.and(poly.or(li).or(m1).or(m2)).separation(poly.or(li).or(m1).or(m2), 1.5, euclidian).polygons.with_area(2.25,nil).output("vpp.5", "vpp.5 : min. vpp spacing to poly or li or m1 or m2 : 1.5um") +vpp.with_area(0..area(vpp.and(m3))*0.25).output("vpp.5a", "vpp.5a : max. m3 density in vpp : 0.25") +vpp.with_area(0..area(vpp.and(m4))*0.3).output("vpp.5b", "vpp.5b : max. m4 density in vpp : 0.3") +vpp.with_area(0..area(vpp.and(m5))*0.4).output("vpp.5c", "vpp.5c : max. m5 density in vpp : 0.4") +nwell.enclosing(vpp, 1.5, euclidian).output("vpp.8", "vpp.8 : nwell enclosure of vpp : 1.5") +vpp.separation(nwell, 1.5, euclidian).polygons.without_area(0).output("vpp.9", "vpp.9 : vpp spacing to nwell : 1.5") +# rule vpp.10 not coded +# rule vpp.11 not coded because moscap is not defined properly by any gds layer +# rules vpp.12a, 12b, 12c not coded because specific to one cell +if backend_flow = CU + m1.separation(vpp.and(m1), 0.16, euclidian).polygons.without_area(0).output("vpp.13", "vpp.13 : m1 spacing to m1inside vpp : 0.16") +end + +# CAPM +capm.width(1.0, euclidian).output("capm.1", "capm.1 : min. capm width : 1.0um") +capm.isolated(0.84, euclidian).output("capm.2a", "capm.2a : min. capm spacing : 0.84um") +m2.interacting(capm).isolated(1.2, euclidian).output("capm.2b", "capm.2b : min. capm spacing : 1.2um") +m2.enclosing(capm, 0.14, euclidian).output("capm.3", "capm.3 : min. m2 enclosure of capm : 0.14um") +capm.enclosing(via2, 0.14, euclidian).output("capm.4", "capm.4 : min. capm enclosure of via2 : 0.14um") +capm.separation(via2, 0.14, euclidian).output("capm.5", "capm.5 : min. capm spacing to via2 : 0.14um") +capm.sized(-20.0).sized(20.0).output("capm.6", "capm.6 : max. capm lenght/width : 20um") +capm.with_angle(0 .. 90).output("capm.7", "capm.7 : capm not rectangle") +capm.separation(via, 0.14, euclidian).polygons.without_area(0).output("capm.8", "capm.8 : min. capm spacing to via : 0.14um") +capm.and(nwell).edges.not(capm.edges).output("capm.10", "capm.10 : capm must not straddle nwell") +capm.and(diff).edges.not(capm.edges).output("capm.10", "capm.10 : capm must not straddle diff") +capm.and(tap).edges.not(capm.edges).output("capm.10", "capm.10 : capm must not straddle tap") +capm.and(poly).edges.not(capm.edges).output("capm.10", "capm.10 : capm must not straddle poly") +capm.and(li).edges.not(capm.edges).output("capm.10", "capm.10 : capm must not straddle li") +capm.and(m1).edges.not(capm.edges).output("capm.10", "capm.10 : capm must not straddle m1") +capm.separation(m2.not_interacting(capm), 0.14, euclidian).output("capm.11", "capm.11 : min. capm spacing to m2 not overlapping capm : 0.5um") + +end #FEOL + +if BEOL +info("BEOL section") + +# li +not_in_cell5 = source.select("-s8rf2_xcmvpp_hd5_*") +not_in_cell5_li = not_in_cell5.polygons(li_wildcard) +not_in_cell5_li.width(0.17, euclidian).output("li.1", "li.1 : min. li width : 0.17um") + +in_cell5_li = li - not_in_cell5_li +in_cell5_li.width(0.14, euclidian).output("li.1a", "li.1a : min. li width for the cells s8rf2_xcmvpp_hd5_* : 0.14um") + +# rule li.2 not coded + +not_in_cell5_li.space(0.17, euclidian).output("li.3", "li.3 : min. li spacing : 0.17um") +in_cell5_li.space(0.14, euclidian).output("li.3a", "li.3a : min. li spacing for the cells s8rf2_xcmvpp_hd5_* : 0.14um") +licon08 = licon.interacting(li.enclosing(licon, 0.08, euclidian).polygons) +licon_edges_with_less_enclosure_li = li.enclosing(licon, 0.08, projection).second_edges +opposite3 = (licon.edges - licon_edges_with_less_enclosure_li).width(0.17 + 1.dbu, projection).polygons +licon08.not_interacting(opposite3).output("li.5", "li.5 : min. li enclosure of licon of 2 opposite edges : 0.08um") +li.with_area(0..0.0561).output("li.6", "li.6 : min. li area : 0.0561um²") + +# ct +mcon.edges.without_length(0.17).output("ct.1", "ct.1 : minimum/maximum width of mcon : 0.17um") +mcon.space(0.19, euclidian).output("ct.2", "ct.2 : min. mcon spacing : 0.19um") +# rule ct.3 not coded +mcon.not(li).output("ct.4", "ct.4 : mcon should covered by li") +if backend_flow = CU + li.interacting(li.and(m1).not(mcon).with_holes(1,10)).enclosing(mcon, 0.2, euclidian).output("ct.irdrop.1", "ct.irdrop.1 : min. li enclsoure of 1..10 mcon : 0.2um") + li.interacting(li.and(m1).not(mcon).with_holes(11,100)).enclosing(mcon, 0.3, euclidian).output("ct.irdrop.2", "ct.irdrop.2 : min. li enclsoure of 11..100 mcon : 0.3um") +end + +# m1 +m1.width(0.14, euclidian).output("m1.1", "m1.1 : min. m1 width : 0.14um") + +huge_m1 = m1.sized(-1.5).sized(1.5) +non_huge_m1 = m1 - huge_m1 + +non_huge_m1.space(0.14, euclidian).output("m1.2", "m1.2 : min. m1 spacing : 0.14um") + +(huge_m1.separation(non_huge_m1, 0.28, euclidian) + huge_m1.space(0.28, euclidian)).output("m1.3ab", "m1.3ab : min. 3um.m1 spacing m1 : 0.28um") + +not_in_cell6 = layout(source.cell_obj).select("-s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b", "-s8fpls_pl8", "-s8fs_cmux4_fm") +not_in_cell6_m1 = not_in_cell6.input(m1_wildcard) +not_in_cell6_m1.enclosing(mcon, 0.03, euclidian).output("m1.4", "m1.4 : min. m1 enclosure of mcon : 0.03um") +in_cell6 = layout(source.cell_obj).select("+s8cell_ee_plus_sseln_a", "+s8cell_ee_plus_sseln_b", "+s8cell_ee_plus_sselp_a", "+s8cell_ee_plus_sselp_b", "+s8fpls_pl8", "+s8fs_cmux4_fm") +in_cell6_m1 = in_cell6.input(m1_wildcard) +in_cell6_m1.enclosing(mcon, 0.005, euclidian).output("m1.4a", "m1.4a : min. m1 enclosure of mcon for specific cells : 0.005um") +m1.with_area(0..0.083).output("m1.6", "m1.6 : min. m1 area : 0.083um²") +m1.holes.with_area(0..0.14).output("m1.7", "m1.7 : min. m1 holes area : 0.14um²") +if backend_flow = AL + mcon06 = mcon.interacting(poly.enclosing(m1, 0.06, euclidian).polygons) + mcon_edges_with_less_enclosure_m1 = m1.enclosing(mcon, 0.06, projection).second_edges + opposite4 = (mcon.edges - mcon_edges_with_less_enclosure_m1).width(0.17 + 1.dbu, projection).polygons + mcon06.not_interacting(opposite4).output("m1.5", "m1.5 : min. m1 enclosure of mcon of 2 opposite edges : 0.06um") + # rule m1.pd.1, rule m1.pd.2a, rule m1.pd.2b not coded +end +if bakend_flow = CU + m1.sized(-2.0).sized(2.0).output("m1.11", "m1.11 : max. m1 width after slotting : 4.0um") + # rule m1.12 not coded because inconsistent with m1.11 + # rule m1.13, m1.14, m1.14a not coded : see : https://www.klayout.de/forum/discussion/comment/6759 +end + +# via +#rule via.3 not coded +via.not(m1).output("via.4c.5c", "via.4c.5c : m1 must enclose all via") +if backend_flow = AL + via.not(areaid_mt).edges.without_length(0.15).output("via.1a", "via.1a : minimum/maximum width of via : 0.15um") + via.and(areaid_mt).not_interacting((via.and(areaid_mt).edges.without_length(0.15)).or(via.and(areaid_mt).edges.without_length(0.23)).or(via.and(areaid_mt).edges.without_length(0.28))).output("via.1b", "via.1b : minimum/maximum width of via in areaid.mt: 0.15um or 0.23um or 0.28um") + via.isolated(0.17, euclidian).output("via.2", "via.2 : min. via spacing : 0.17um") + m1.enclosing(via.not_interacting(via.edges.without_length(0.15)), 0.055, euclidian).output("via.4a", "via.4a : min. m1 enclosure of 0.15um via : 0.055um") + m1.enclosing(via.not_interacting(via.edges.without_length(0.23)), 0.03, euclidian).output("via.4b", "via.4b : min. m1 enclosure of 0.23um via : 0.03um") + via1_edges_with_less_enclosure_m1 = m1.enclosing(via.not_interacting(via.edges.without_length(0.15)), 0.085, projection).second_edges + opposite5 = (via.not_interacting(via.edges.without_length(0.15)).edges - via1_edges_with_less_enclosure_m1).width(0.15 + 1.dbu, projection).polygons + via.not_interacting(via.edges.without_length(0.15)).not_interacting(opposite5).output("via1.5a", "via1.5a : min. m1 enclosure of 0.15um via of 2 opposite edges : 0.085um") + via2_edges_with_less_enclosure_m1 = m1.enclosing(via.not_interacting(via.edges.without_length(0.23)), 0.06, projection).second_edges + opposite6 = (via.not_interacting(via.edges.without_length(0.23)).edges - via2_edges_with_less_enclosure_m1).width(0.23 + 1.dbu, projection).polygons + via.not_interacting(via.edges.without_length(0.23)).not_interacting(opposite6).output("via1.5b", "via1.5b : min. m1 enclosure of 0.23um via of 2 opposite edges : 0.06um") +end +if backend_flow = CU + via.not(areaid_mt).edges.without_length(0.18).output("via.11", "via.11 : minimum/maximum width of via : 0.18um") + via.isolated(0.13, euclidian).output("via.12", "via.12 : min. via spacing : 0.13um") + # rule via.13 not coded because not understandable + via1_edges_with_less_enclosure_m1 = m1.enclosing(via, 0.04, projection).second_edges + opposite5 = (via.edges - via1_edges_with_less_enclosure_m1).width(0.18 + 1.dbu, projection).polygons + via.not_interacting(opposite5).output("via1.14", "via1.14 : min. m1 enclosure of 0.04um via of 2 opposite edges : 0.04um") + # rules via.irdrop.1, via.irdrop.2, via.irdrop.3, via.irdrop.4 not coded because not understandable +end + +# m2 +m2.width(0.14, euclidian).output("m2.1", "m2.1 : min. m2 width : 0.14um") + +huge_m2 = m2.sized(-1.5).sized(1.5) +non_huge_m2 = m2 - huge_m2 + +non_huge_m2.space(0.14, euclidian).output("m2.2", "m2.2 : min. m2 spacing : 0.14um") + +(huge_m2.separation(non_huge_m2, 0.28, euclidian) + huge_m2.space(0.28, euclidian)).output("m2.3ab", "m2.3ab : min. 3um.m2 spacing m2 : 0.28um") + +# rule m2.3c not coded +m2.with_area(0..0.0676).output("m2.6", "m2.6 : min. m2 area : 0.0676um²") +m2.holes.with_area(0..0.14).output("m2.7", "m2.7 : min. m2 holes area : 0.14um²") +via.not(m2).output("m2.via", "m2.via : m2 must enclose via") +if backend_flow = AL + m2.enclosing(via, 0.055, euclidian).output("m2.4", "m2.4 : min. m2 enclosure of via : 0.055um") + via_edges_with_less_enclosure_m2 = m2.enclosing(via, 0.085, projection).second_edges + opposite7 = (via.edges - via_edges_with_less_enclosure_m2).width(0.2 + 1.dbu, projection).polygons + via.not_interacting(opposite7).output("m2.5", "m2.5 : min. m2 enclosure of via of 2 opposite edges : 0.085um") + # rule m2.pd.1, rule m2.pd.2a, rule m2.pd.2b not coded +end +if bakend_flow = CU + m2.sized(-2.0).sized(2.0).output("m2.11", "m2.11 : max. m2 width after slotting : 4.0um") + # rule m2.12 not coded because inconsistent with m2.11 + # rule m2.13, m2.14, m2.14a not coded : see : https://www.klayout.de/forum/discussion/comment/6759 +end + +# via2 +#rule via233 not coded +via2.not(m2).output("via2", "via2 : m2 must enclose all via2") +if backend_flow = AL + via2.not(areaid_mt).edges.without_length(0.2).output("via2.1a", "via2.1a : minimum/maximum width of via2 : 0.2um") + via2.and(areaid_mt).not_interacting((via2.and(areaid_mt).edges.without_length(0.2)).or(via2.and(areaid_mt).edges.without_length(1.2)).or(via2.and(areaid_mt).edges.without_length(1.5))).output("via2.1b", "via2.1b : minimum/maximum width of via2 in areaid.mt: 0.2um or 1.2um or 1.5um") + via2.isolated(0.2, euclidian).output("via2.2", "via2.2 : min. via2 spacing : 0.2um") + m2.enclosing(via2, 0.04, euclidian).output("via2.4", "via2.4 : min. m2 enclosure of via2 : 0.04um") + m2.enclosing(via2.not_interacting(via2.edges.without_length(1.5)), 0.14, euclidian).output("via2.4a", "via2.4a : min. m2 enclosure of 1.5um via2 : 0.14um") + via2_edges_with_less_enclosure_m2 = m2.enclosing(via2, 0.085, projection).second_edges + opposite8 = (via2.edges - via2_edges_with_less_enclosure_m2).width(0.2 + 1.dbu, projection).polygons + via2.not_interacting(opposite8).output("via2.5", "via2.5 : min. m2 enclosure of via2 of 2 opposite edges : 0.085um") +end +if backend_flow = CU + via2.edges.without_length(0.21).output("via2.11", "via2.11 : minimum/maximum width of via2 : 0.21um") + via2.isolated(0.18, euclidian).output("via2.12", "via2.12 : min. via2 spacing : 0.18um") + # rule via2.13 not coded because not understandable, or not clear + m2.enclosing(via2, 0.035, euclidian).output("via2.14", "via2.14 : min. m2 enclosure of via2 : 0.035um") + # rules via2.irdrop.1, via2.irdrop.2, via2.irdrop.3, via2.irdrop.4 not coded because not understandable +end + +# m3 +m3.width(0.3, euclidian).output("m3.1", "m3.1 : min. m3 width : 0.3um") + +huge_m3 = m3.sized(-1.5).sized(1.5) +non_huge_m3 = m3 - huge_m3 + +non_huge_m3.space(0.3, euclidian).output("m3.2", "m3.2 : min. m3 spacing : 0.3um") + +(huge_m3.separation(non_huge_m3, 0.4, euclidian) + huge_m3.space(0.4, euclidian)).output("m3.3ab", "m3.3ab : min. 3um.m3 spacing m3 : 0.4um") + +# rule m3.3c not coded +m3.with_area(0..0.24).output("m3.6", "m3.6 : min. m2 area : 0.24um²") +via2.not(m3).output("m3.via2", "m3.via2 : m3 must enclose via2") +if backend_flow = AL + m3.enclosing(via2, 0.065, euclidian).output("m3.4", "m3.4 : min. m3 enclosure of via2 : 0.065um") + via2_edges_with_less_enclosure_m3 = m3.enclosing(via2, 0.085, projection).second_edges + # m3.5 N/A + # opposite9 = (via2.edges - via2_edges_with_less_enclosure_m3).width(0.3 + 1.dbu, projection).polygons + # via2.not_interacting(opposite9).output("m3.5", "m3.5 : min. m3 enclosure of via2 of 2 opposite edges : 0.085um") + # rule m3.pd.1, rule m3.pd.2a, rule m3.pd.2b not coded +end +if bakend_flow = CU + m3.holes.with_area(0..0.2).output("m3.7", "m3.7 : min. m2 holes area : 0.2um²") + m3.sized(-2.0).sized(2.0).output("m3.11", "m3.11 : max. m3 width after slotting : 4.0um") + # rule m3.12 not coded because inconsistent with m3.11 + # rule m3.13, m3.14, m3.14a not coded : see : https://www.klayout.de/forum/discussion/comment/6759 +end + +# via3 +#rule via3.3 not coded +via3.not(m3).output("via3", "via3 : m3 must enclose all via3") +if backend_flow = AL + via3.not(areaid_mt).edges.without_length(0.2).output("via3.1a", "via3.1a : minimum/maximum width of via3 : 0.2um") + via3.and(areaid_mt).not_interacting((via3.and(areaid_mt).edges.without_length(0.2)).or(via3.and(areaid_mt).edges.without_length(0.8))).output("via3.1a", "via3.1a : minimum/maximum width of via3 in areaid.mt: 0.2um or 0.8um") + via3.isolated(0.2, euclidian).output("via3.2", "via3.2 : min. via3 spacing : 0.2um") + m3.enclosing(via3, 0.06, euclidian).output("via3.4", "via3.4 : min. m3 enclosure of via3 : 0.06um") + via3_edges_with_less_enclosure_m3 = m3.enclosing(via3, 0.09, projection).second_edges + opposite10 = (via3.edges - via3_edges_with_less_enclosure_m3).width(0.2 + 1.dbu, projection).polygons + via3.not_interacting(opposite10).output("via3.5", "via3.5 : min. m2 enclosure of via3 of 2 opposite edges : 0.09um") +end +if backend_flow = CU + via3.edges.without_length(0.21).output("via3.11", "via3.11 : minimum/maximum width of via3 : 0.21um") + via3.isolated(0.18, euclidian).output("via3.12", "via3.12 : min. via3 spacing : 0.18um") + m3.enclosing(via3, 0.055, euclidian).output("via3.13", "via3.13 : min. m3 enclosure of via3 : 0.055um") + # rule via3.14 not coded because not understandable, or not clear + # rules via3.irdrop.1, via3.irdrop.2, via3.irdrop.3, via3.irdrop.4 not coded because not understandable +end + +# m4 +m4.width(0.3, euclidian).output("m4.1", "m4.1 : min. m4 width : 0.3um") + +huge_m4 = m4.sized(-1.5).sized(1.5) +non_huge_m4 = m4 - huge_m4 + +non_huge_m4.space(0.3, euclidian).output("m4.2", "m4.2 : min. m4 spacing : 0.3um") + +(huge_m4.separation(non_huge_m4, 0.4, euclidian) + huge_m4.space(0.4, euclidian)).output("m4.5ab", "m4.5ab : min. 3um.m4 spacing m4 : 0.4um") + +m4.with_area(0..0.24).output("m4.4", "m4.4 : min. m2 area : 0.24um²") +via3.not(m4).output("m4.via3", "m4.via3 : m4 must enclose via3") +if backend_flow = AL + m4.enclosing(via3, 0.065, euclidian).output("m4.3", "m4.3 : min. m4 enclosure of via3 : 0.065um") + # m4.5 doesn't exist + # via3_edges_with_less_enclosure_m4 = m4.enclosing(via2, 0.085, projection).second_edges + # opposite9 = (via3.edges - via3_edges_with_less_enclosure_m4).width(0.3 + 1.dbu, projection).polygons + # via3.not_interacting(opposite9).output("m4.5", "m4.5 : min. m4 enclosure of via3 of 2 opposite edges : 0.085um") + # rule m4.pd.1, rule m4.pd.2a, rule m4.pd.2b not coded +end +if bakend_flow = CU + m4.holes.with_area(0..0.2).output("m4.7", "m4.7 : min. m2 holes area : 0.2um²") + m4.sized(-5.0).sized(5.0).output("m4.11", "m4.11 : max. m4 width after slotting : 10.0um") + # rule m4.12 not coded because inconsistent with m4.11 + # rule m4.13, m4.14, m4.14a not coded : see : https://www.klayout.de/forum/discussion/comment/6759 + m4.enclosing(via3, 0.06, euclidian).output("m4.15", "m4.15 : min. m4 enclosure of via3 : 0.06um") +end + +# via4 +via4.edges.without_length(0.8).output("via4.1a", "via4.1a : minimum/maximum width of via4 : 0.8um") +via4.isolated(0.8, euclidian).output("via4.2", "via4.2 : min. via4 spacing : 0.8um") +#rule via4.3 not coded +m4.enclosing(via4, 0.19, euclidian).output("via4.4", "via4.4 : min. m4 enclosure of via4 : 0.19um") +via4.not(m4).output("via4", "via4 : m4 must enclose all via4") +if backend_flow = CU + # rules via4.irdrop.1, via4.irdrop.2, via4.irdrop.3, via4.irdrop.4 not coded because not understandable +end + +# m5 +m5.width(1.6, euclidian).output("m5.1", "m5.1 : min. m5 width : 1.6um") + +m5.space(1.6, euclidian).output("m5.2", "m5.2 : min. m5 spacing : 1.6um") + +via4.not(m5).output("m5.via4", "m5.via4 : m5 must enclose via4") +m5.enclosing(via4, 0.31, euclidian).output("m5.3", "m4.3 : min. m5 enclosure of via4 : 0.31um") + +# nsm +nsm.width(3.0, euclidian).output("nsm.1", "nsm.1 : min. nsm width : 3.0um") +nsm.isolated(4.0, euclidian).output("nsm.2", "nsm.2 : min. nsm spacing : 4.0um") +nsm.enclosing(diff, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of diff : 3.0um") +nsm.enclosing(tap, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of tap : 3.0um") +nsm.enclosing(poly, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of poly : 3.0um") +nsm.enclosing(li, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of li : 3.0um") +nsm.enclosing(m1, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of m1 : 3.0um") +nsm.enclosing(m2, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of m2 : 3.0um") +nsm.enclosing(m3, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of m3 : 3.0um") +nsm.enclosing(m4, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of m4 : 3.0um") +nsm.enclosing(m5, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of m5 : 3.0um") +nsm.enclosing(cfom, 3.0, euclidian).output("nsm.4", "nsm.4 : min. nsm enclosure of cfom : 3.0um") +if backend_flow = AL + nsm.separation(diff, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to diff : 1.0um") + nsm.separation(tap, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to tap : 1.0um") + nsm.separation(poly, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to poly : 1.0um") + nsm.separation(li, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to li : 1.0um") + nsm.separation(m1, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to m1 : 1.0um") + nsm.separation(m2, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to m2 : 1.0um") + nsm.separation(m3, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to m3 : 1.0um") + nsm.separation(m4, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to m4 : 1.0um") + nsm.separation(m5, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to m5 : 1.0um") + nsm.separation(cfom, 1.0, euclidian).output("nsm.3", "nsm.3 : min. nsm spacing to cfom : 1.0um") +end + +# pad +pad.isolated(1.27, euclidian).output("pad.2", "pad.2 : min. pad spacing : 1.27um") + +end #BEOL + +if FEOL +info("FEOL section") + +# mf +mf.not_interacting(mf.edges.without_length(0.8)).output("mf.1", "mf.1 : minimum/maximum width of fuse : 0.8um") +mf.not_interacting(mf.edges.without_length(7.2)).output("mf.2", "mf.2 : minimum/maximum length of fuse : 7.2um") +mf.isolated(1.96, euclidian).output("mf.3", "mf.3 : min. fuse center spacing : 2.76um") +# fuses need more clarification on fuse_shield, fuse layers ... + +# hvi +hvi.width(0.6, euclidian).output("hvi.1", "hvi.1 : min. hvi width : 0.6um") +hvi.isolated(0.7, euclidian).output("hvi.2", "hvi.2 : min. hvi spacing, merge if less : 0.7um") +hvi.and(tunm).output("hvi.4", "hvi.4 : hvi must not overlapp tunm") +hvi.and(nwell).separation(nwell, 2.0, euclidian).output("hvnwell.8", "hvnwelli.8 : min. hvnwel spacing to nwell : 2.0") +areaid_hl.not(hvi).output("hvnwel.9", "hvnwell.9 : hvi must overlapp hvnwell") +# rule hvnell.10 not coded +diff.not(psdm.and(diff_rs)).and(hvi).width(0.29, euclidian).output("hvdifftap.14", "hvdifftap.14 : min. diff inside hvi width : 0.29um") +diff.and(psdm.and(diff_rs)).and(hvi).width(0.15, euclidian).output("hvdifftap.14a", "hvdifftap.14a : min. p+diff resistor inside hvi width : 0.15um") +diff.and(hvi).isolated(0.3, euclidian).output("hvdifftap.15a", "hvdifftap.15a : min. diff inside hvi spacing : 0.3um") +diff.and(hvi).and(nsdm).separation(diff.and(hvi).and(psdm), 0.37, euclidian).polygons.without_area(0).output("hvdifftap.15b", "hvdifftap.15b : min. n+diff inside hvi spacing to p+diff inside hvi except abutting: 0.37um") +tap.and(hvi).edges.and(diff).without_length(0.7).output("hvdifftap.16", "hvdifftap.16 : min. tap inside hvi abuttng diff : 0.7um") +hvi.and(nwell).enclosing(diff, 0.33, euclidian).output("hvdifftap.17", "hvdifftap.17 : min. hvnwell enclosure of p+diff : 0.33um") +hvi.and(nwell).separation(diff, 0.43, euclidian).output("hvdifftap.18", "hvdifftap.18 : min. hvnwell spacing to n+diff : 0.43um") +hvi.and(nwell).enclosing(tap, 0.33, euclidian).output("hvdifftap.19", "hvdifftap.19 : min. hvnwell enclosure of n+tap : 0.33um") +hvi.and(nwell).separation(tap, 0.43, euclidian).output("hvdifftap.20", "hvdifftap.20 : min. hvnwell spacing to p+tap : 0.43um") +hvi.and(diff).edges.not(diff.edges).output("hvdifftap.21", "hvdifftap.21 : diff must not straddle hvi") +hvi.and(tap).edges.not(tap.edges).output("hvdifftap.21", "hvdifftap.21 : tap must not straddle hvi") +hvi.enclosing(difftap, 0.18, euclidian).output("hvdifftap.22", "hvdifftap.22 : min. hvi enclosure of diff or tap : 0.18um") +hvi.separation(difftap, 0.18, euclidian).output("hvdifftap.23", "hvdifftap.23 : min. hvi spacing to diff or tap : 0.18um") +hvi.and(diff).not(nwell).separation(nwell, 0.43, euclidian).output("hvdifftap.24", "hvdifftap.24 : min. hv n+diff spacing to nwell : 0.43um") +diff.and(hvi).not(nwell).isolated(1.07, euclidian).polygons.and(tap).output("hvdifftap.25", "hvdifftap.25 : min. n+diff inside hvi spacing accros p+tap : 1.07um") +diff.not(poly).edges.and(gate.and(hvi).edges).space(0.35, euclidian).output("hvpoly.13", "hvpoly.13: min. hvi gate length : 0.5um") +hvi.and(poly).edges.not(poly.edges).output("hvpoly.14", "hvpoly.14 : poly must not straddle hvi") + +# hvntm +hvntm.width(0.7, euclidian).output("hvntm.1", "hvntm.1 : min. hvntm width : 0.7um") +hvntm.isolated(0.7, euclidian).output("hvntm.2", "hvntm.2 : min. hvntm spacing : 0.7um") +hvntm.enclosing(diff.and(nwell).and(hvi), 0.185, euclidian).output("hvntm.3", "hvntm.3 : min. hvntm enclosure of hv n+diff : 0.185um") +hvntm.separation(diff.not(nwell).not(hvi), 0.185, euclidian).output("hvntm.4", "hvntm.4 : min. hvntm spacing to n+diff : 0.185um") +hvntm.separation(diff.and(nwell).not(hvi), 0.185, euclidian).output("hvntm.5", "hvntm.5 : min. hvntm spacing to p+diff : 0.185um") +hvntm.separation(tap.not(nwell).not(hvi), 0.185, euclidian).polygons.without_area(0).output("hvntm.6a", "hvntm.6a : min. hvntm spacing to p+tap : 0.185um") +hvntm.and(areaid_ce).output("hvntm.9", "hvntm.9 : hvntm must not overlapp areaid.ce") + +# denmos +poly.not_interacting(pwde).interacting(areaid_en).width(1.055, projection).output("denmos.1", "denmos.1 : min. de_nfet gate width : 1.055um") +diff.not_interacting(pwde).enclosing(poly.interacting(areaid_en), 0.28, projection).polygons.without_area(0).output("denmos.2", "denmos.2 : min. de_nfet source ouside poly width : 0.28um") +diff.not_interacting(pwde).and(poly.interacting(areaid_en)).width(0.925, projection).output("denmos.3", "denmos.3 : min. de_nfet source inside poly width : 0.925um") +diff.not_interacting(pwde).interacting(areaid_en).not_interacting(poly).width(0.17, euclidian).output("denmos.4", "denmos.4 : min. de_nfet drain width : 0.17um") +nwell.not_interacting(pwde).and(poly.interacting(areaid_en)).width(0.225, projection).polygons.or(nwell.and(poly.interacting(areaid_en)).sized(-0.1125).sized(0.1125)).output("denmos.5", "denmos.5 : min. de_nfet source inside nwell width : 0.225m") +diff.not_interacting(pwde).interacting(areaid_en).not_interacting(poly).separation(diff.interacting(poly.interacting(areaid_en)), 1.585, projection).output("denmos.6", "denmos.6 : min. de_nfet source spacing to drain : 1.585um") +nwell.not_interacting(pwde).and(poly.and(diff).interacting(areaid_en)).edges.without_length(5.0, nil).output("denmos.7", "denmos.7 : min. de_nfet channel width : 5.0um") +diff.not_interacting(pwde).interacting(areaid_en).not_interacting(poly).edges.without_angle(45).without_angle(135).without_angle(225).without_angle(315).output("denmos.8", "denmos.8 : 90deg. not allowed for de_nfet drain") +nwell.not_interacting(pwde).interacting(areaid_en).edges.without_angle(45).without_angle(135).without_angle(225).without_angle(315).output("denmos.9a", "denmos.9a : 90deg. not allowed for de_nfet nwell") +nwell.not_interacting(pwde).interacting(areaid_en).edges.with_angle(45).without_length(0.607..0.609).output("denmos.9a", "denmos.9a : 45deg. bevels of de_nfet nwell should be 0.43um from corners") +nwell.not_interacting(pwde).interacting(areaid_en).edges.with_angle(135).without_length(0.607..0.609).output("denmos.9a", "denmos.9a : 45deg. bevels of de_nfet nwell should be 0.43um from corners") +diff.not_interacting(pwde).interacting(areaid_en).not_interacting(poly).edges.with_angle(45).without_length(0.7..0.71).output("denmos.9b", "denmos.9b : 45deg. bevels of de_nfet drain should be 0.05um from corners") +diff.not_interacting(pwde).interacting(areaid_en).not_interacting(poly).edges.with_angle(135).without_length(0.7..0.71).output("denmos.9b", "denmos.9b : 45deg. bevels of de_nfet drain should be 0.05um from corners") +nwell.not_interacting(pwde).enclosing(diff.interacting(areaid_en).not_interacting(poly), 0.66, euclidian).output("denmos.10", "denmos.10 : min. nwell enclosure of de_nfet drain : 0.66um") +nwell.not_interacting(pwde).interacting(areaid_en).separation(tap.not(nwell), 0.86, euclidian).output("denmos.11", "denmos.11 : min. de_nfet nwell spacing to tap : 0.86um") +nwell.not_interacting(pwde).interacting(areaid_en).isolated(2.4, euclidian).output("denmos.12", "denmos.12 : min. de_nfet nwell : 2.4um") +nsdm.not_interacting(pwde).enclosing(diff.interacting(areaid_en).interacting(poly), 0.13, euclidian).output("denmos.13", "denmos.13 : min. nsdm enclosure of de_nfet source : 0.13um") + +# depmos +poly.interacting(pwde).interacting(areaid_en).width(1.05, projection).output("depmos.1", "depmos.1 : min. de_pfet gate width : 1.05um") +diff.interacting(pwde).enclosing(poly.interacting(areaid_en), 0.28, projection).polygons.without_area(0).output("depmos.2", "depmos.2 : min. de_pfet source ouside poly width : 0.28um") +diff.interacting(pwde).and(poly.interacting(areaid_en)).width(0.92, projection).output("depmos.3", "depmos.3 : min. de_pfet source inside poly width : 0.92um") +diff.interacting(pwde).interacting(areaid_en).not_interacting(poly).width(0.17, euclidian).output("depmos.4", "depmos.4 : min. de_pfet drain width : 0.17um") +pwde.not(nwell).and(poly.interacting(areaid_en)).width(0.26, projection).polygons.or(pwde.not(nwell).and(poly.interacting(areaid_en)).sized(-0.13).sized(0.13)).output("depmos.5", "depmos.5 : min. de_pfet source inside nwell width : 0.26m") +diff.interacting(pwde).interacting(areaid_en).not_interacting(poly).separation(diff.interacting(poly.interacting(areaid_en)), 1.19, projection).output("depmos.6", "depmos.6 : min. de_pfet source spacing to drain : 1.19um") +nwell.interacting(pwde).and(poly.and(diff).interacting(areaid_en)).edges.without_length(5.0, nil).output("depmos.7", "depmos.7 : min. de_pfet channel width : 5.0um") +diff.interacting(pwde).interacting(areaid_en).not_interacting(poly).edges.without_angle(45).without_angle(135).without_angle(225).without_angle(315).output("depmos.8", "depmos.8 : 90deg. not allowed for de_pfet drain") +pwde.not(nwell).interacting(areaid_en).edges.without_angle(45).without_angle(135).without_angle(225).without_angle(315).output("depmos.9a", "depmos.9a : 90deg. not allowed for de_pfet pwell") +pwde.not(nwell).interacting(areaid_en).edges.with_angle(45).without_length(0.607..0.609).output("depmos.9a", "depmos.9a : 45deg. bevels of de_pfet pwell should be 0.43um from corners") +pwde.not(nwell).interacting(areaid_en).edges.with_angle(135).without_length(0.607..0.609).output("depmos.9a", "depmos.9a : 45deg. bevels of de_pfet pwell should be 0.43um from corners") +diff.interacting(pwde).interacting(areaid_en).not_interacting(poly).edges.with_angle(45).without_length(0.7..0.71).output("depmos.9b", "depmos.9b : 45deg. bevels of de_pfet drain should be 0.05um from corners") +diff.interacting(pwde).interacting(areaid_en).not_interacting(poly).edges.with_angle(135).without_length(0.7..0.71).output("depmos.9b", "depmos.9b : 45deg. bevels of de_pfet drain should be 0.05um from corners") +nwell.interacting(pwde).separation(diff.interacting(areaid_en).not_interacting(poly), 0.86, euclidian).output("depmos.10", "depmos.10 : min. pwell enclosure of de_pfet drain : 0.86um") +pwde.not(nwell).interacting(areaid_en).separation(tap.and(nwell), 0.66, euclidian).output("depmos.11", "depmos.11 : min. de_pfet pwell spacing to tap : 0.66um") +psdm.interacting(pwde).enclosing(diff.interacting(areaid_en).interacting(poly), 0.13, euclidian).output("depmos.12", "depmos.12 : min. psdm enclosure of de_pfet source : 0.13um") + +# extd +areaid_en.and(difftap).edges.not(difftap.edges).output("extd.1", "extd.1 : difftap must not straddle areaid.en") +difftap.interacting(areaid_en).not(poly).with_area(0).output("extd.2", "extd.2 : poly must not overlapp entirely difftap in areaid.en") +# rules extd.4, extd.5, extd.6, extd.7 not coded because specific to some cells + +# vhvi +# rules vhvi.vhv.1, vhvi.vhv.2, vhvi.vhv.3, vhvi.vhv.4, vhvi.vhv.5, vhvi.vhv.6 not coded +vhvi.width(0.02, euclidian).output("vhvi.1", "vhvi.1 : min. vhvi width : 0.02um") +vhvi.and(areaid_ce).output("vhvi.2", "vhvi.2 : vhvi must not overlap areaid.ce") +vhvi.and(hvi).output("vhvi.3", "vhvi.3 : vhvi must not overlap hvi") +# rules vhvi.4, vhvi.6 not coded +vhvi.and(diff).edges.not(diff.edges).output("vhvi.5", "vhvi.5 : vhvi must not straddle diff") +vhvi.and(tap).edges.not(tap.edges).output("vhvi.5", "vhvi.5 : vhvi must not straddle tap") +vhvi.and(poly).edges.not(poly.edges).output("vhvi.7", "vhvi.7 : vhvi must not straddle poly") + +nwell.and(vhvi).separation(nwell, 2.5, euclidian).output("hv.nwell.1", "hv.nwell.1 : min. vhvi nwell spacing to nwell : 2.5um") +diff.and(vhvi).isolated(0.3, euclidian).output("hv.diff.1", "hv.diff.1 : min. vhvi diff spacing : 0.3um") +nwell.interacting(diff.and(vhvi)).separation(diff.not(nwell), 0.43, euclidian).output("hv.diff.2", "hv.diff.2 : min. vhvi nwell spacing n+diff : 0.43um") +diff.and(vhvi).not(nwell).separation(nwell, 0.55, euclidian).output("hv.diff.3a", "hv.diff.3a : min. vhvi n+diff spacing nwell : 0.55um") +# rule hv.diff.3b not coded +poly.and(vhvi).not(diff).separation(diff, 0.3, euclidian).polygons.without_area(0).output("hv.poly.2", "hv.poly.2 : min. vhvi poly spacing to diff : 0.3um") +poly.and(vhvi).not(diff).separation(nwell, 0.55, euclidian).polygons.without_area(0).output("hv.poly.3", "hv.poly.3 : min. vhvi poly spacing to nwell : 0.55um") +nwell.enclosing(poly.and(vhvi).not(diff), 0.3, euclidian).polygons.without_area(0).output("hv.poly.4", "hv.poly.4 : min. nwell enclosure of vhvi poly : 0.3um") +#poly.and(vhvi).enclosing(diff.interacting(areaid_en), 0.16, projection).polygons.without_area(0).output("hv.poly.6", "hv.poly.6 : min. poly enclosure of hvfet gate : 0.16um") +# rule hv.poly.7 not coded + +# uhvi +uhvi.and(diff).edges.not(diff.edges).output("uhvi.1", "uhvi.1 : diff must not straddle uhvi") +uhvi.and(tap).edges.not(tap.edges).output("uhvi.1", "uhvi.1 : tap must not straddle uhvi") +uhvi.and(poly).edges.not(poly.edges).output("uhvi.2", "uhvi.2 : poly must not straddle uhvi") +pwbm.not(uhvi).output("uhvi.3", "uhvi.3 : uhvi must not enclose pwbm") +uhvi.and(dnwell).edges.not(dnwell.edges).output("uhvi.4", "uhvi.4 : dnwell must not straddle uhvi") +areaid_en20.not(uhvi).output("uhvi.5", "uhvi.5 : uhvi must not enclose areaid.en20") +#dnwell.not(uhvi).output("uhvi.6", "uhvi.6 : uhvi must not enclose dnwell") +natfet.not(uhvi).output("uhvi.7", "uhvi.7 : uhvi must not enclose natfet") + +# pwell_res +pwell_rs.width(2.65).output("pwres.2", "pwres.2 : min. pwell resistor width : 2.65um") +pwell_rs.sized(-2.65).sized(2.65).output("pwres.2", "pwres.2 : max. pwell resistor width : 2.65um") +pwell_rs.interacting(pwell_rs.edges.with_length(2.651,26.499)).output("pwres.3", "pwres.3 : min. pwell resistor length : 26.5um") +pwell_rs.interacting(pwell_rs.edges.with_length(265.0, nil)).output("pwres.4", "pwres.4 : max. pwell resistor length : 265um") +tap.interacting(pwell_rs).separation(nwell, 0.22, euclidian).output("pwres.5", "pwres.5 : min. pwell resistor tap spacing to nwell : 0.22um") +tap.interacting(pwell_rs).and(tap.sized(0.22).and(nwell)).output("pwres.5", "pwres.5 : max. pwell resistor tap spacing to nwell : 0.22um") +tap.interacting(pwell_rs).width(0.53).output("pwres.6", "pwres.6 : min. width of tap inside pwell resistor : 0.53um") +tap.interacting(pwell_rs).sized(-0.265).sized(0.265).output("pwres.6", "pwres.6 : max. width of tap inside pwell resistor : 0.53um") +# rules pwres.7a, pwres.7b not coded +pwell_rs.and(diff).output("pwres.8", "pwres.8 : diff not allowed inside pwell resistor") +pwell_rs.and(poly).output("pwres.8", "pwres.8 : poly not allowed inside pwell resistor") +# rules pwres.9, pwres.10 not coded + +# rf_diode +areaid_re.with_angle(0 .. 90).output("rfdiode.1", "rfdiode.1 : non 90 degree angle areaid.re") +areaid_re.not(nwell).or(nwell.interacting(areaid_re).not(areaid_re)).output("rfdiode.2", "rfdiode.2 : areaid.re must coincide rf nwell diode") +# rule rfdiode.3 not coded + +end #FEOL + +if OFFGRID +info("OFFGRID-ANGLES section") + +dnwell.ongrid(0.005).output("dnwell_OFFGRID", "x.1b : OFFGRID vertex on dnwell") +dnwell.with_angle(0 .. 45).output("dnwell_angle", "x.3a : non 45 degree angle dnwell") +nwell.ongrid(0.005).output("nwell_OFFGRID", "x.1b : OFFGRID vertex on nwell") +nwell.with_angle(0 .. 45).output("nwell_angle", "x.3a : non 45 degree angle nwell") +pwbm.ongrid(0.005).output("pwbm_OFFGRID", "x.1b : OFFGRID vertex on pwbm") +pwbm.with_angle(0 .. 45).output("pwbm_angle", "x.3a : non 45 degree angle pwbm") +pwde.ongrid(0.005).output("pwde_OFFGRID", "x.1b : OFFGRID vertex on pwde") +pwde.with_angle(0 .. 45).output("pwde_angle", "x.3a : non 45 degree angle pwde") +hvtp.ongrid(0.005).output("hvtp_OFFGRID", "x.1b : OFFGRID vertex on hvtp") +hvtp.with_angle(0 .. 45).output("hvtp_angle", "x.3a : non 45 degree angle hvtp") +hvtr.ongrid(0.005).output("hvtr_OFFGRID", "x.1b : OFFGRID vertex on hvtr") +hvtr.with_angle(0 .. 45).output("hvtr_angle", "x.3a : non 45 degree angle hvtr") +lvtn.ongrid(0.005).output("lvtn_OFFGRID", "x.1b : OFFGRID vertex on lvtn") +lvtn.with_angle(0 .. 45).output("lvtn_angle", "x.3a : non 45 degree angle lvtn") +ncm.ongrid(0.005).output("ncm_OFFGRID", "x.1b : OFFGRID vertex on ncm") +ncm.with_angle(0 .. 45).output("ncm_angle", "x.3a : non 45 degree angle ncm") +diff.ongrid(0.005).output("diff_OFFGRID", "x.1b : OFFGRID vertex on diff") +tap.ongrid(0.005).output("tap_OFFGRID", "x.1b : OFFGRID vertex on tap") +diff.not(areaid_en.and(uhvi)).with_angle(0 .. 90).output("diff_angle", "x.2 : non 90 degree angle diff") +diff.and(areaid_en.and(uhvi)).with_angle(0 .. 45).output("diff_angle", "x.2c : non 45 degree angle diff") +tap.not(areaid_en.and(uhvi)).with_angle(0 .. 90).output("tap_angle", "x.2 : non 90 degree angle tap") +tap.and(areaid_en.and(uhvi)).with_angle(0 .. 45).output("tap_angle", "x.2c : non 45 degree angle tap") +tunm.ongrid(0.005).output("tunm_OFFGRID", "x.1b : OFFGRID vertex on tunm") +tunm.with_angle(0 .. 45).output("tunm_angle", "x.3a : non 45 degree angle tunm") +poly.ongrid(0.005).output("poly_OFFGRID", "x.1b : OFFGRID vertex on poly") +poly.with_angle(0 .. 90).output("poly_angle", "x.2 : non 90 degree angle poly") +rpm.ongrid(0.005).output("rpm_OFFGRID", "x.1b : OFFGRID vertex on rpm") +rpm.with_angle(0 .. 45).output("rpm_angle", "x.3a : non 45 degree angle rpm") +npc.ongrid(0.005).output("npc_OFFGRID", "x.1b : OFFGRID vertex on npc") +npc.with_angle(0 .. 45).output("npc_angle", "x.3a : non 45 degree angle npc") +nsdm.ongrid(0.005).output("nsdm_OFFGRID", "x.1b : OFFGRID vertex on nsdm") +nsdm.with_angle(0 .. 45).output("nsdm_angle", "x.3a : non 45 degree angle nsdm") +psdm.ongrid(0.005).output("psdm_OFFGRID", "x.1b : OFFGRID vertex on psdm") +psdm.with_angle(0 .. 45).output("psdm_angle", "x.3a : non 45 degree angle psdm") +licon.ongrid(0.005).output("licon_OFFGRID", "x.1b : OFFGRID vertex on licon") +licon.with_angle(0 .. 90).output("licon_angle", "x.2 : non 90 degree angle licon") +li.ongrid(0.005).output("li_OFFGRID", "x.1b : OFFGRID vertex on li") +li.with_angle(0 .. 45).output("li_angle", "x.3a : non 45 degree angle li") +mcon.ongrid(0.005).output("ct_OFFGRID", "x.1b : OFFGRID vertex on mcon") +mcon.with_angle(0 .. 90).output("ct_angle", "x.2 : non 90 degree angle mcon") +vpp.ongrid(0.005).output("vpp_OFFGRID", "x.1b : OFFGRID vertex on vpp") +vpp.with_angle(0 .. 45).output("vpp_angle", "x.3a : non 45 degree angle vpp") +m1.ongrid(0.005).output("m1_OFFGRID", "x.1b : OFFGRID vertex on m1") +m1.with_angle(0 .. 45).output("m1_angle", "x.3a : non 45 degree angle m1") +via.ongrid(0.005).output("via_OFFGRID", "x.1b : OFFGRID vertex on via") +via.with_angle(0 .. 90).output("via_angle", "x.2 : non 90 degree angle via") +m2.ongrid(0.005).output("m2_OFFGRID", "x.1b : OFFGRID vertex on m2") +m2.with_angle(0 .. 45).output("m2_angle", "x.3a : non 45 degree angle m2") +via2.ongrid(0.005).output("via2_OFFGRID", "x.1b : OFFGRID vertex on via2") +via2.with_angle(0 .. 90).output("via2_angle", "x.2 : non 90 degree angle via2") +m3.ongrid(0.005).output("m3_OFFGRID", "x.1b : OFFGRID vertex on m3") +m3.with_angle(0 .. 45).output("m3_angle", "x.3a : non 45 degree angle m3") +via3.ongrid(0.005).output("via3_OFFGRID", "x.1b : OFFGRID vertex on via3") +via3.with_angle(0 .. 90).output("via3_angle", "x.2 : non 90 degree angle via3") +nsm.ongrid(0.005).output("nsm_OFFGRID", "x.1b : OFFGRID vertex on nsm") +nsm.with_angle(0 .. 45).output("nsm_angle", "x.3a : non 45 degree angle nsm") +m4.ongrid(0.005).output("m4_OFFGRID", "x.1b : OFFGRID vertex on m4") +m4.with_angle(0 .. 45).output("m4_angle", "x.3a : non 45 degree angle m4") +via4.ongrid(0.005).output("via4_OFFGRID", "x.1b : OFFGRID vertex on via4") +via4.with_angle(0 .. 90).output("via4_angle", "x.2 : non 90 degree angle via4") +m5.ongrid(0.005).output("m5_OFFGRID", "x.1b : OFFGRID vertex on m5") +m5.with_angle(0 .. 45).output("m5_angle", "x.3a : non 45 degree angle m5") +pad.ongrid(0.005).output("pad_OFFGRID", "x.1b : OFFGRID vertex on pad") +pad.with_angle(0 .. 45).output("pad_angle", "x.3a : non 45 degree angle pad") +mf.ongrid(0.005).output("mf_OFFGRID", "x.1b : OFFGRID vertex on mf") +mf.with_angle(0 .. 90).output("mf_angle", "x.2 : non 90 degree angle mf") +hvi.ongrid(0.005).output("hvi_OFFGRID", "x.1b : OFFGRID vertex on hvi") +hvi.with_angle(0 .. 45).output("hvi_angle", "x.3a : non 45 degree angle hvi") +hvntm.ongrid(0.005).output("hvntm_OFFGRID", "x.1b : OFFGRID vertex on hvntm") +hvntm.with_angle(0 .. 45).output("hvntm_angle", "x.3a : non 45 degree angle hvntm") +vhvi.ongrid(0.005).output("vhvi_OFFGRID", "x.1b : OFFGRID vertex on vhvi") +vhvi.with_angle(0 .. 45).output("vhvi_angle", "x.3a : non 45 degree angle vhvi") +uhvi.ongrid(0.005).output("uhvi_OFFGRID", "x.1b : OFFGRID vertex on uhvi") +uhvi.with_angle(0 .. 45).output("uhvi_angle", "x.3a : non 45 degree angle uhvi") +pwell_rs.ongrid(0.005).output("pwell_rs_OFFGRID", "x.1b : OFFGRID vertex on pwell_rs") +pwell_rs.with_angle(0 .. 45).output("pwell_rs_angle", "x.3a : non 45 degree angle pwell_rs") +areaid_re.ongrid(0.005).output("areaid_re_OFFGRID", "x.1b : OFFGRID vertex on areaid.re") + +end #OFFGRID + diff --git a/technology/sky130/tech/sky130.lylvs b/technology/sky130/tech/sky130.lylvs new file mode 100644 index 00000000..00526f9b --- /dev/null +++ b/technology/sky130/tech/sky130.lylvs @@ -0,0 +1,278 @@ + + + + + lvs + + + + false + false + + true + lvs_scripts + tools_menu.lvs.end + dsl + lvs-dsl-xml + # +tstart = Time.now +# Extraction for SKY130 +# +############################ + +# optionnal for a batch launch : klayout -b -rd input=my_layout.gds -rd report=my_report.lyrdb -rd schematic=reference_netlist.cir -rd target_netlist=extracted_netlist.cir -r lvs_sky130.lvs +if $input + source($input) +end + +if $report + report_lvs($report) +else + report_lvs("lvs_report.lvsdb") +end + +if $schematic +#reference netlist + schematic($schematic) +else + schematic(RBA::CellView::active.filename.sub(/\.(oas|gds|oas.gz|gds.gz)$/, ".sp")) +end + +# true: use net names instead of numbers +# false: use numbers for nets +spice_with_net_names = true + +# true: put in comments with details +# false: no comments +spice_with_comments = true + +if $target_netlist + target_netlist($target_netlist) +else + target_netlist(File.join(File.dirname(RBA::CellView::active.filename), source.cell_name+"_extracted.cir"), write_spice(spice_with_net_names, spice_with_comments), "Extracted by KLayout on : #{Time.now.strftime("%d/%m/%Y %H:%M")}") +end + +# klayout setup +######################## +# Hierarchical mode +deep +# Use 4 CPU cores +threads(4) +# Print details +verbose(true) + +# layers definitions +######################## + + +# LVS section +######################## +info("LVS section") +# layers definitions +######################## +BOUND = polygons(235, 4) +DNWELL = polygons(64, 18) +PWRES = polygons(64, 13) +NWELL = polygons(64, 20) +NWELLTXT = input(64, 5) +NWELLPIN = polygons(64, 16) +SUBTXT = input(122, 5) +SUBPIN = input(64, 59) +DIFF = polygons(65, 20) +TAP = polygons(65, 44) +PSDM = polygons(94, 20) +NSDM = polygons(93, 44) +LVTN = polygons(125, 44) +HVTR = polygons(18, 20) +HVTP = polygons(78, 44) +SONOS = polygons(80, 20) +COREID = polygons(81, 2) +STDCELL = polygons(81, 4) +NPNID = polygons(82, 20) +PNPID = polygons(82, 44) +RPM = polygons(86, 20) +URPM = polygons(79, 20) +LDNTM = polygons(11, 44) +HVNTM = polygons(125, 20) +POLY = polygons(66, 20) +POLYTXT = input(66, 5) +POLYPIN = polygons(66, 16) +HVI = polygons(75, 20) +LICON = polygons(66, 44) +NPC = polygons(95, 20) +DIFFRES = polygons(65, 13) +POLYRES = polygons(66, 13) +POLYSHO = polygons(66, 15) +DIODE = polygons(81, 23) +LI = polygons(67, 20) +LITXT = input(67, 5) +LIPIN = polygons(67, 16) +LIRES = polygons(67, 13) +MCON = polygons(67, 44) +MET1 = polygons(68, 20) +MET1TXT = input(68, 5) +MET1PIN = polygons(68, 16) +MET1RES = polygons(68, 13) +VIA1 = polygons(68, 44) +MET2 = polygons(69, 20) +MET2TXT = input(69, 5) +MET2PIN = polygons(69, 16) +MET2RES = polygons(69, 13) +VIA2 = polygons(69, 44) +MET3 = polygons(70, 20) +MET3TXT = input(70, 5) +MET3PIN = polygons(70, 16) +MET3RES = polygons(70, 13) +VIA3 = polygons(70, 44) +MET4 = polygons(71, 20) +MET4TXT = input(71, 5) +MET4PIN = polygons(71, 16) +MET4RES = polygons(71, 13) +VIA4 = polygons(71, 44) +MET5 = polygons(72, 20) +MET5TXT = input(72, 5) +MET5PIN = polygons(72, 16) +MET5RES = polygons(72, 13) +RDL = polygons(74, 20) +RDLTXT = input(74, 5) +RDLPIN = polygons(74, 16) +GLASS = polygons(76, 20) +CAPM = polygons(89, 44) +CAPM2 = polygons(97, 44) +LOWTAPD = polygons(81, 14) +FILLOBSM1 = polygons(62, 24) +FILLOBSM2 = polygons(105, 52) +FILLOBSM3 = polygons(107, 24) +FILLOBSM4 = polygons(112, 4) +NCM = polygons(92, 44) + +# Bulk layer for terminal provisioning +SUB = polygons(236, 0) +# SUB = polygon_layer + +# Computed layers +PDIFF = DIFF & NWELL & PSDM +NTAP = TAP & NWELL & NSDM +PGATE = PDIFF & POLY +PSD = PDIFF - PGATE +CORE_PGATE = PGATE & COREID +STD_PGATE = PGATE - HVTP - NCM - HVI - COREID +HVT_PGATE = PGATE & HVTP - NCM - HVI +HV5_PGATE = PGATE - HVTP - NCM & HVI + +NDIFF = DIFF - NWELL & NSDM +PTAP = TAP - NWELL & PSDM +NGATE = NDIFF & POLY +NSD = NDIFF - NGATE +CORE_NGATE = NGATE & COREID +STD_NGATE = NGATE - NCM - LVTN - HVI +LVT_NGATE = NGATE - NCM & LVTN - HVI +HV5_NGATE = NGATE - NCM - LVTN & HVI +HV5NA_NGATE = NGATE - NCM & LVTN & HVI + +# drawing to physical +device_scaling(1000000) + +# PMOS transistor device extraction +extract_devices(mos4("sky130_fd_pr__special_pfet_latch"), { "SD" => PSD, "G" => CORE_PGATE, "tS" => PSD, "tD" => PSD, "tG" => POLY, "W" => NWELL }) +extract_devices(mos4("sky130_fd_pr__pfet_01v8"), { "SD" => PSD, "G" => STD_PGATE, "tS" => PSD, "tD" => PSD, "tG" => POLY, "W" => NWELL }) +extract_devices(mos4("sky130_fd_pr__pfet_01v8_hvt"), { "SD" => PSD, "G" => HVT_PGATE, "tS" => PSD, "tD" => PSD, "tG" => POLY, "W" => NWELL }) +extract_devices(mos4("sky130_fd_pr__pfet_g5v0d10v5"), { "SD" => PSD, "G" => HV5_PGATE, "tS" => PSD, "tD" => PSD, "tG" => POLY, "W" => NWELL }) + +# NMOS transistor device extraction +extract_devices(mos4("sky130_fd_pr__special_nfet_latch"), { "SD" => NSD, "G" => CORE_NGATE, "tS" => NSD, "tD" => NSD, "tG" => POLY, "W" => SUB }) +extract_devices(mos4("sky130_fd_pr__nfet_01v8"), { "SD" => NSD, "G" => STD_NGATE, "tS" => NSD, "tD" => NSD, "tG" => POLY, "W" => SUB }) +extract_devices(mos4("sky130_fd_pr__nfet_01v8_lvt"), { "SD" => NSD, "G" => LVT_NGATE, "tS" => NSD, "tD" => NSD, "tG" => POLY, "W" => SUB }) +extract_devices(mos4("sky130_fd_pr__nfet_g5v0d10v5"), { "SD" => NSD, "G" => HV5_NGATE, "tS" => NSD, "tD" => NSD, "tG" => POLY, "W" => SUB }) +extract_devices(mos4("sky130_fd_pr__nfet_01v8_nvt"), { "SD" => NSD, "G" => HV5NA_NGATE, "tS" => NSD, "tD" => NSD, "tG" => POLY, "W" => SUB }) + + +# Define connectivity for netlist extraction + +# Inter-layer +connect(SUB, PTAP) +connect(NWELL, NTAP) +connect(LICON, PTAP) +connect(LICON, NTAP) +connect(PSD, LICON) +connect(NSD, LICON) +connect(POLY, LICON) +connect(LICON, LI) +connect(LI, MCON) +connect(MCON, MET1) +connect(MET1,VIA1) +connect(VIA1, MET2) +connect(MET2, VIA2) +connect(VIA2, MET3) +connect(MET3, VIA3) +connect(VIA3, MET4) +connect(MET4, VIA4) +connect(VIA4, MET5) +# Attaching labels +connect(SUB, SUBTXT) +connect(SUB, SUBPIN) +connect(NWELL, NWELLTXT) +connect(POLY, POLYTXT) +connect(LI, LITXT) +connect(MET1, MET1TXT) +connect(MET2, MET2TXT) +connect(MET3, MET3TXT) +connect(MET4, MET4TXT) +connect(MET5, MET5TXT) + +# Global +connect_global(SUB, "gnd") + +if $connect_supplies +connect_implicit("*", "vdd") +connect_implicit("*", "gnd") +end + +#connect_global(pwell, "PWELL") +#connect_global(nwell, "NWELL") +#connect_global(bulk, "BULK") + +# Actually performs the extraction +netlist # ... not really required + +# Flatten cells which are present in one netlist only +align +# SIMPLIFICATION of the netlist +#netlist.make_top_level_pins +#netlist.combine_devices +#netlist.purge +#netlist.purge_nets +netlist.simplify +#schematic.simplify + +# Tolerances for the devices extracted parameters +# tolerance(device_class_name, parameter_name [, :absolute => absolute_tolerance] [, :relative => relative_tolerance]) +tolerance("pfet_01v8", "W", :absolute => 1.nm, :relative => 0.001) +tolerance("pfet_01v8", "L", :absolute => 1.nm, :relative => 0.001) +tolerance("pfet_01v8_hvt", "W", :absolute => 1.nm, :relative => 0.001) +tolerance("pfet_01v8_hvt", "L", :absolute => 1.nm, :relative => 0.001) +tolerance("nfet_01v8", "W", :absolute => 1.nm, :relative => 0.001) +tolerance("nfet_01v8", "L", :absolute => 1.nm, :relative => 0.001) +tolerance("nfet_01v8_lvt", "W", :absolute => 1.nm, :relative => 0.001) +tolerance("nfet_01v8_lvt", "L", :absolute => 1.nm, :relative => 0.001) + +#max_res(1000000) +#min_caps(1e-15) + +max_branch_complexity(65536) +max_depth(16) + +if ! compare + #raise "ERROR : Netlists don't match" + puts "ERROR : Netlists don't match" +else + puts "CONGRATULATIONS! Netlists match." +end + +# time spent for the LVS +time = Time.now +hours = ((time - tstart)/3600).to_i +minutes = ((time - tstart)/60 - hours * 60).to_i +seconds = ((time - tstart) - (minutes * 60 + hours * 3600)).to_i +$stdout.write "LVS finished at : #{time.hour}:#{time.min}:#{time.sec} - LVS duration = #{hours} hrs. #{minutes} min. #{seconds} sec.\n" + diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index a2c4d185..8eea88f1 100644 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -7,10 +7,7 @@ import os -from design_rules import * -from module_type import * -from custom_cell_properties import cell_properties, cell -from custom_layer_properties import layer_properties +import drc as d """ File containing the process technology parameters for Skywater 130nm. @@ -25,7 +22,7 @@ File containing the process technology parameters for Skywater 130nm. # Using tech_modules['cellname'] you can override each class by providing a custom # implementation in '$OPENRAM_TECHDIR/modules/' # For example: tech_modules["contact"] = "contact_freepdk45" -tech_modules = module_type() +tech_modules = d.module_type() # These modules have been hand designed and provided in this repository. tech_modules["nand2_dec"] = "nand2_dec" @@ -68,7 +65,7 @@ tech_modules["and4_dec"] = "and4_dec" ################################################### # Custom cell properties ################################################### -cell_properties = cell_properties() +cell_properties = d.cell_properties() cell_properties.bitcell_power_pin_directions = ("H", "H") @@ -86,6 +83,13 @@ cell_properties.bitcell_1port.port_map = {'bl': 'BL', 'vpb': 'VPB', 'gnd': 'VGND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.bl_layer = "m1" +cell_properties.bitcell_1port.vdd_layer = "m1" +cell_properties.bitcell_1port.vdd_dir = "V" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" + cell_properties.bitcell_2port.mirror.x = True cell_properties.bitcell_2port.mirror.y = True cell_properties.bitcell_2port.end_caps = True @@ -98,30 +102,43 @@ cell_properties.bitcell_2port.port_map = {'bl0': 'BL0', 'wl1': 'WL1', 'vdd': 'VDD', 'gnd': 'GND'} +cell_properties.bitcell_1port.wl_layer = "m2" +cell_properties.bitcell_1port.vdd_layer = "m2" +cell_properties.bitcell_1port.vdd_dir = "H" +cell_properties.bitcell_1port.gnd_layer = "m2" +cell_properties.bitcell_1port.gnd_dir = "H" +cell_properties.bitcell_2port.wl_layer = "m2" +cell_properties.bitcell_2port.vdd_layer = "m1" +cell_properties.bitcell_2port.vdd_dir = "H" +cell_properties.bitcell_2port.gnd_layer = "m2" +cell_properties.bitcell_2port.gnd_dir = "H" -cell_properties.col_cap_1port_bitcell = cell(['br', 'vdd', 'gnd', 'bl'], - ['INPUT', 'INPUT', 'GROUND', 'POWER'], - {'bl': 'BL0', - 'br': 'BL1', - 'vdd': 'VPWR', - 'gnd': 'VGND'}) +cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'vdd', 'gnd', 'br', 'gate', 'vpb', 'vnb'], + ['INPUT', 'POWER', 'GROUND', 'INPUT', 'INPUT', 'BIAS', 'BIAS'], + {'bl': 'bl', + 'br': 'br', + 'vdd': 'vdd', + 'gnd': 'gnd', + 'gate': 'gate', + 'vnb': 'vnb', + 'vpb': 'vpb'}) cell_properties.col_cap_1port_bitcell.boundary_layer = "mem" -cell_properties.col_cap_1port_strap_power = cell(['vdd', 'vpb', 'vnb'], +cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'], ['POWER', 'BIAS', 'BIAS'], {'vnb': 'VNB', 'vpb': 'VPB', 'vdd': 'VPWR'}) cell_properties.col_cap_1port_strap_power.boundary_layer = "mem" -cell_properties.col_cap_1port_strap_ground = cell(['gnd', 'vpb', 'vnb'], +cell_properties.col_cap_1port_strap_ground = d.cell(['gnd', 'vpb', 'vnb'], ['GROUND', 'BIAS', 'BIAS'], {'vnb': 'VNB', 'vpb': 'VPB', 'gnd': 'VGND'}) cell_properties.col_cap_1port_strap_ground.boundary_layer = "mem" -cell_properties.row_cap_1port_cell = cell(['vdd', 'wl'], +cell_properties.row_cap_1port_cell = d.cell(['vdd', 'wl'], ['POWER', 'INPUT'], {'wl': 'WL', 'vdd': 'VPWR'}) @@ -215,10 +232,11 @@ cell_properties.names["write_driver"] = "sky130_fd_bd_sram__openram_write_driver array_row_multiple = 2 array_col_multiple = 2 + ################################################### # Custom layer properties ################################################### -layer_properties = layer_properties() +layer_properties = d.layer_properties() layer_properties.hierarchical_decoder.bus_layer = "m1" layer_properties.hierarchical_decoder.bus_directions = "nonpref" layer_properties.hierarchical_decoder.input_layer = "li" @@ -449,7 +467,7 @@ parameter["6T_inv_nmos_size"] = 0.205 parameter["6T_inv_pmos_size"] = 0.09 parameter["6T_access_size"] = 0.135 -drc = design_rules("sky130") +drc = d.design_rules("sky130") # grid size drc["grid"] = 0.005 @@ -460,6 +478,7 @@ drc["grid"] = 0.005 NDA_PDK_ROOT = os.environ.get("NDA_PDK_ROOT", False) use_calibre = bool(NDA_PDK_ROOT) use_calibre = False +use_klayout = False if use_calibre: # Correct order according to s8 pin_purpose = 16 @@ -747,8 +766,10 @@ if use_calibre: drc_name = "calibre" lvs_name = "calibre" pex_name = "calibre" - # Calibre automatically scales to micron to SI units and requires mult parameter - lvs_lib = "calibre_lvs_lib" +elif use_klayout: + drc_name = "klayout" + lvs_name = "klayout" + pex_name = "klayout" else: drc_name = "magic" lvs_name = "netgen"