Merge branch 'dev' into stable

This commit is contained in:
mrg 2022-07-21 09:37:24 -07:00
commit 0d616ae072
473 changed files with 11595 additions and 3032 deletions

View File

@ -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/*

160
Makefile
View File

@ -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

24
PORTING.md Normal file
View File

@ -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.

150
README.md
View File

@ -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 <techname>" 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/<technology>/<test>
```
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])
+ <a rel="me" href="https://fosstodon.org/@mrg">@mrg@fostodon.org</a>
# 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

View File

@ -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 {} \;

21
compiler/base/__init__.py Normal file
View File

@ -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 *

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -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:

View File

@ -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):

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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

View File

@ -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']
return c_relative/parameter['cap_relative_per_ff']

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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):

View File

@ -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))

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1 @@
from .datasheet_gen import datasheet_gen

View File

@ -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

View File

@ -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:

View File

@ -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"""

6
compiler/drc/__init__.py Normal file
View File

@ -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 *

View File

@ -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():
"""

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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] <config file>\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):

View File

@ -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 *

View File

@ -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:

View File

@ -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"

View File

@ -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())

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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
return False

View File

@ -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 col<self.column_size-1 and row>0 and row<self.row_size-1:
self.trim_insts.add(name)
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""

View File

@ -7,18 +7,18 @@
#
import debug
import design
from base import design
from globals import OPTS
import logical_effort
from base import logical_effort
from tech import parameter, drc, layer, spice
class bitcell_base(design.design):
class bitcell_base(design):
"""
Base bitcell parameters to be over-riden.
"""
def __init__(self, name, cell_name=None, prop=None):
design.design.__init__(self, name, cell_name, prop)
design.__init__(self, name, cell_name, prop)
# Set the bitcell specific properties
if prop:
@ -37,12 +37,12 @@ class bitcell_base(design.design):
# min size NMOS gate load
read_port_load = 0.5
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 analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
@ -264,4 +264,4 @@ class bitcell_base(design.design):
else:
delay = math.sqrt(2*tstep*(vdd-spice["nom_threshold"])/m)
return delay
return delay

View File

@ -6,12 +6,12 @@
# All rights reserved.
#
import debug
import design
from base import design
from sram_factory import factory
from globals import OPTS
class bitcell_base_array(design.design):
class bitcell_base_array(design):
"""
Abstract base class for bitcell-arrays -- bitcell, dummy, replica
"""
@ -43,11 +43,11 @@ class bitcell_base_array(design.design):
# Make a flat list too
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
def create_all_wordline_names(self, row_size=None):
def create_all_wordline_names(self, row_size=None, start_row=0):
if row_size == None:
row_size = self.row_size
for row in range(row_size):
for row in range(start_row, row_size):
for port in self.all_ports:
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
@ -156,18 +156,15 @@ class bitcell_base_array(design.design):
width=self.width,
height=wl_pin.height())
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]
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def route_supplies(self):
for inst in self.cell_inst.values():
for pin_name in ["vdd", "gnd"]:
self.copy_layout_pin(inst, pin_name)
def add_layout_pins(self):
""" Add the layout pins """
self.add_bitline_pins()
self.add_wl_pins()
self.add_supply_pins()
def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset

View File

@ -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
@ -42,14 +42,13 @@ class col_cap_array(bitcell_base_array):
self.height = self.dummy_cell.height
self.width = self.column_size * self.cell.width
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
self.add_mod(self.dummy_cell)
def create_instances(self):
""" Create the module instances used in this design """
@ -100,5 +99,4 @@ class col_cap_array(bitcell_base_array):
inst = self.cell_inst[row, col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.copy_power_pin(pin)
self.copy_layout_pin(inst, pin_name)

View File

@ -7,16 +7,16 @@
#
import debug
from tech import cell_properties as props
import bitcell_base
from .bitcell_base import bitcell_base
class col_cap_bitcell_1port(bitcell_base.bitcell_base):
class col_cap_bitcell_1port(bitcell_base):
"""
Column end cap cell.
"""
def __init__(self, name="col_cap_bitcell_1port"):
bitcell_base.bitcell_base.__init__(self, name, prop=props.col_cap_1port)
bitcell_base.__init__(self, name, prop=props.col_cap_1port)
debug.info(2, "Create col_cap bitcell 1 port object")
self.no_instances = True

View File

@ -7,16 +7,16 @@
#
import debug
from tech import cell_properties as props
import bitcell_base
from .bitcell_base import bitcell_base
class col_cap_bitcell_2port(bitcell_base.bitcell_base):
class col_cap_bitcell_2port(bitcell_base):
"""
Column end cap cell.
"""
def __init__(self, name="col_cap_bitcell_2port"):
bitcell_base.bitcell_base.__init__(self, name, prop=props.col_cap_2port)
bitcell_base.__init__(self, name, prop=props.col_cap_2port)
debug.info(2, "Create col_cap bitcell 2 port object")
self.no_instances = True

View File

@ -0,0 +1,119 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2022 Regents of the University of California
# All rights reserved.
#
from tech import drc
import debug
from base import design
import math
from sram_factory import factory
from base import vector
from globals import OPTS
from tech import cell_properties
from tech import layer_properties as layer_props
class column_decoder(design):
"""
Create the column mux decoder.
"""
def __init__(self, name, col_addr_size):
super().__init__(name)
self.col_addr_size = col_addr_size
self.num_inputs = col_addr_size
self.num_outputs = pow(2, col_addr_size)
debug.info(2,
"create column decoder of {0} inputs and {1} outputs".format(self.num_inputs,
self.num_outputs))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
self.DRC_LVS()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_instances()
def create_instances(self):
self.column_decoder_inst = self.add_inst(name="column_decoder",
mod=self.column_decoder)
self.connect_inst(self.pins)
def create_layout(self):
self.column_decoder_inst.place(vector(0,0))
self.width = self.column_decoder_inst.width
self.height = self.column_decoder_inst.height
self.route_layout()
def add_pins(self):
""" Add the module pins """
for i in range(self.num_inputs):
self.add_pin("in_{0}".format(i), "INPUT")
for j in range(self.num_outputs):
self.add_pin("out_{0}".format(j), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def route_layout_pins(self):
""" Add the pins. """
if self.col_addr_size == 1:
self.copy_layout_pin(self.column_decoder_inst, "A", "in_0")
self.copy_layout_pin(self.column_decoder_inst, "Zb", "out_0")
self.copy_layout_pin(self.column_decoder_inst, "Z", "out_1")
elif self.col_addr_size > 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)

View File

@ -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:

View File

@ -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 """

View File

@ -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])

View File

@ -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())

View File

@ -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
"""

View File

@ -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")

View File

@ -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

View File

@ -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):

View File

@ -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",

View File

@ -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):

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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):
"""

View File

@ -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

View File

@ -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,

View File

@ -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
return nmos_drain_c + pmos_drain_c

View File

@ -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
return nmos_drain_c + pmos_drain_c

View File

@ -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
return nmos_drain_c + pmos_drain_c

View File

@ -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 """

View File

@ -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
return True

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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())

View File

@ -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",

View File

@ -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 = []

View File

@ -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

View File

@ -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
self.tx_mults)
return nmos_drain_c + pmos_drain_c

View File

@ -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)

View File

@ -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)

View File

@ -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
self.tx_mults)
return nmos_drain_c + pmos_drain_c

View File

@ -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
self.tx_mults)
return nmos_drain_c + pmos_drain_c

View File

@ -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
return nmos_drain_c + pmos_drain_c

View File

@ -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. """

View File

@ -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

View File

@ -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()

View File

@ -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())

Some files were not shown because too many files have changed in this diff Show More