mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into stable
This commit is contained in:
commit
0d616ae072
|
|
@ -1,56 +1,38 @@
|
||||||
name: ci
|
name: ci
|
||||||
on: [push]
|
on: [push]
|
||||||
jobs:
|
jobs:
|
||||||
scn4me_subm:
|
regress:
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v1
|
uses: actions/checkout@v1
|
||||||
- name: SCMOS test
|
- name: Docker build
|
||||||
|
run: |
|
||||||
|
cd ${{ github.workspace }}/docker
|
||||||
|
make build
|
||||||
|
- name: PDK Install
|
||||||
run: |
|
run: |
|
||||||
. /home/github-runner/setup-paths.sh
|
|
||||||
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
||||||
export OPENRAM_TECH="${{ github.workspace }}/technology:/software/PDKs/skywater-tech"
|
export OPENRAM_TECH="${{ github.workspace }}/technology"
|
||||||
export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp"
|
#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
|
#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
|
- name: Archive
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: scn4me_subm Archives
|
name: Regress Archives
|
||||||
path: ${{ github.workspace }}/*.zip
|
path: ${{ github.workspace }}/compiler/tests/results/*
|
||||||
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/
|
|
||||||
|
|
|
||||||
160
Makefile
160
Makefile
|
|
@ -1,78 +1,111 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# Copyright 2020 Regents of the University of California
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
# The top directory where environment will be created.
|
|
||||||
TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
|
TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))
|
||||||
|
include $(TOP_DIR)/openram.mk
|
||||||
|
|
||||||
.DEFAULT_GOAL := all
|
.DEFAULT_GOAL := install
|
||||||
|
|
||||||
# Skywater PDK SRAM library
|
# Skywater PDK SRAM library
|
||||||
#SRAM_LIBRARY ?= $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_bd_sram
|
SRAM_LIB_DIR ?= $(PDK_ROOT)/sky130_fd_bd_sram
|
||||||
SRAM_GIT_REPO ?= git@github.com:google/skywater-pdk-libs-sky130_fd_bd_sram.git
|
# Use this for release
|
||||||
SRAM_LIBRARY ?= $(TOP_DIR)/sky130_fd_bd_sram
|
SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git
|
||||||
# Open PDKs
|
# Use this for development
|
||||||
OPEN_PDKS ?= $(PDK_ROOT)/sky130A
|
#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
|
# Create lists of all the files to copy/link
|
||||||
GDS_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.gds))
|
GDS_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.gds))
|
||||||
MAG_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.mag))
|
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_SUFFIX := spice
|
||||||
SPICE_LVS_SUFFIX := lvs.$(SPICE_SUFFIX)
|
SPICE_LVS_SUFFIX := lvs.$(SPICE_SUFFIX)
|
||||||
SPICE_CALIBRE_SUFFIX := lvs.calibre.$(SPICE_SUFFIX)
|
SPICE_CALIBRE_SUFFIX := lvs.calibre.$(SPICE_SUFFIX)
|
||||||
|
SPICE_KLAYOUT_SUFFIX := lvs.klayout.$(SPICE_SUFFIX)
|
||||||
SPICE_BASE_SUFFIX := base.$(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_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)
|
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_BASE := $(OPENRAM_HOME)/../technology/sky130
|
||||||
INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS))
|
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):
|
$(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR)
|
||||||
git clone $(SRAM_GIT_REPO) $(SRAM_LIBRARY)
|
@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 "Installing sky130 SRAM PDK..."
|
||||||
@echo "PDK_ROOT='$(PDK_ROOT)'"
|
@echo "PDK_ROOT='$(PDK_ROOT)'"
|
||||||
@echo "SRAM_LIBRARY='$(SRAM_LIBRARY)'"
|
@echo "SRAM_LIB_DIR='$(SRAM_LIB_DIR)'"
|
||||||
@echo "OPEN_PDKS='$(OPEN_PDKS)'"
|
@echo "SKY130_PDK='$(SKY130_PDK)'"
|
||||||
make install
|
@make $(INSTALL_DIRS)
|
||||||
|
.PHONY: install
|
||||||
|
|
||||||
|
pdk: $(SKY130_PDK)
|
||||||
@true
|
@true
|
||||||
|
.PHONY: pdk
|
||||||
|
|
||||||
$(INSTALL_BASE)/gds_lib: $(GDS_FILES)
|
$(INSTALL_BASE)/gds_lib: $(GDS_FILES)
|
||||||
@echo
|
@echo
|
||||||
@echo "Setting up GDS cell library for OpenRAM."
|
@echo "Setting up GDS cell library for OpenRAM."
|
||||||
@echo "=================================================================="
|
@echo "=================================================================="
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
@cp -va $? $@
|
cp -va $? $@
|
||||||
@echo "=================================================================="
|
@echo "=================================================================="
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
|
|
@ -124,6 +157,18 @@ $(INSTALL_BASE)/calibre_lvs_lib: $(filter %.$(SPICE_CALIBRE_SUFFIX),$(ALL_SPICE_
|
||||||
@echo "=================================================================="
|
@echo "=================================================================="
|
||||||
@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))
|
$(INSTALL_BASE)/sp_lib: $(filter-out %.$(SPICE_LVS_SUFFIX) %.$(SPICE_CALIBRE_SUFFIX),$(ALL_SPICE_FILES))
|
||||||
@echo
|
@echo
|
||||||
@echo "Setting up spice simulation library for OpenRAM."
|
@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 "=================================================================="
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
|
macros:
|
||||||
|
cd macros && make
|
||||||
|
|
||||||
|
.PHONY: macros
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(SRAM_LIBRARY)
|
@rm -f *.zip
|
||||||
rm -f $(INSTALL_BASE)/tech/.magicrc
|
.PHONE: clean
|
||||||
rm -f $(INSTALL_BASE)/mag_lib/.magicrc
|
|
||||||
rm -f $(INSTALL_BASE)/lef_lib/.magicrc
|
uninstall: clean
|
||||||
rm -f $(INSTALL_BASE)/maglef_lib/.magicrc
|
@rm -f $(INSTALL_BASE)/tech/.magicrc
|
||||||
rm -rf $(INSTALL_DIRS)
|
@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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
136
README.md
136
README.md
|
|
@ -28,22 +28,31 @@ things that need to be fixed.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
The OpenRAM compiler has very few dependencies:
|
Please see the Dockerfile for the required versions of tools.
|
||||||
+ [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
|
In general, the OpenRAM compiler has very few dependencies:
|
||||||
|
+ Docker
|
||||||
|
+ Make
|
||||||
|
+ Python 3.6 or higher
|
||||||
+ Various Python packages (pip install -r requirements.txt)
|
+ Various Python packages (pip install -r requirements.txt)
|
||||||
+ [Git]
|
+ [Git]
|
||||||
|
|
||||||
If you want to perform DRC and LVS, you will need either:
|
## Docker
|
||||||
+ Calibre (for [FreePDK45])
|
|
||||||
+ [Magic] 8.3.130 or newer
|
We have a [docker setup](./docker) to run OpenRAM. To use this, you should run:
|
||||||
+ [Netgen] 1.5.164 or newer
|
```
|
||||||
|
cd openram/docker
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
This must be run once and will take a while to build all the tools.
|
||||||
|
|
||||||
|
|
||||||
|
## Environment
|
||||||
|
|
||||||
You must set two environment variables:
|
You must set two environment variables:
|
||||||
+ OPENRAM\_HOME should point to the compiler source directory.
|
+ OPENRAM\_HOME should point to the compiler source directory.
|
||||||
+ OPENERAM\_TECH should point to one or more root technology directories (colon separated).
|
+ OPENERAM\_TECH should point to one or more root technology directories (colon separated).
|
||||||
|
|
||||||
## Environment
|
|
||||||
|
|
||||||
For example add this to your .bashrc:
|
For example add this to your .bashrc:
|
||||||
|
|
||||||
|
|
@ -52,25 +61,36 @@ For example add this to your .bashrc:
|
||||||
export OPENRAM_TECH="$HOME/openram/technology"
|
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
|
We include the tech files necessary for [SCMOS] SCN4M_SUBM,
|
||||||
[SCMOS] spice models, however, are generic and should be replaced with
|
[FreePDK45]. The [SCMOS] spice models, however, are
|
||||||
foundry models. If you are using [FreePDK45], you should also have
|
generic and should be replaced with foundry models. You may get the
|
||||||
that set up and have the environment variable point to the PDK. For
|
entire [FreePDK45 PDK here][FreePDK45].
|
||||||
example add this to your .bashrc:
|
|
||||||
|
|
||||||
```
|
|
||||||
export FREEPDK45="/bsoe/software/design-kits/FreePDK45"
|
|
||||||
```
|
|
||||||
|
|
||||||
You may get the entire [FreePDK45 PDK here][FreePDK45].
|
### Sky130 Setup
|
||||||
If you are using [SCMOS], you should install [Magic] and [Netgen].
|
|
||||||
We have included the most recent SCN4M_SUBM design rules from [Qflow].
|
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
|
# Basic Usage
|
||||||
|
|
||||||
|
|
@ -79,7 +99,6 @@ using a single configuration file written in Python.
|
||||||
|
|
||||||
For example, create a file called *myconfig.py* specifying the following
|
For example, create a file called *myconfig.py* specifying the following
|
||||||
parameters for your memory:
|
parameters for your memory:
|
||||||
|
|
||||||
```
|
```
|
||||||
# Data word size
|
# Data word size
|
||||||
word_size = 2
|
word_size = 2
|
||||||
|
|
@ -116,6 +135,12 @@ python3 $OPENRAM_HOME/openram.py myconfig
|
||||||
You can see all of the options for the configuration file in
|
You can see all of the options for the configuration file in
|
||||||
$OPENRAM\_HOME/options.py
|
$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
|
# Unit Tests
|
||||||
|
|
||||||
Regression testing performs a number of tests for all modules in OpenRAM.
|
Regression testing performs a number of tests for all modules in OpenRAM.
|
||||||
|
|
@ -123,50 +148,39 @@ From the unit test directory ($OPENRAM\_HOME/tests),
|
||||||
use the following command to run all regression 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 run a specific technology:
|
||||||
|
|
||||||
To increase the verbosity of the test, add one (or more) -v options:
|
|
||||||
```
|
```
|
||||||
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
|
Unit test results are put in a directory:
|
||||||
|
```
|
||||||
If you want to support a new technology, you will need to create:
|
openram/compiler/tests/results/<technology>/<test>
|
||||||
+ a setup script for each technology you want to use
|
```
|
||||||
+ a technology directory for each technology with the base cells
|
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.
|
||||||
We provide two technology examples for [SCMOS] and [FreePDK45]. Each
|
You can view the .out file to see what the output of a test is in either case.
|
||||||
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.
|
|
||||||
|
|
||||||
# Get Involved
|
# Get Involved
|
||||||
|
|
||||||
|
+ [Port it](./PORTING.md) to a new technology.
|
||||||
+ Report bugs by submitting [Github issues].
|
+ Report bugs by submitting [Github issues].
|
||||||
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
|
+ 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]
|
||||||
|
|
@ -180,6 +194,7 @@ specific technology (e.g., [FreePDK45]) should be a subdirectory
|
||||||
+ [OpenRAM Slack Workspace][Slack]
|
+ [OpenRAM Slack Workspace][Slack]
|
||||||
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
||||||
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-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
|
||||||
|
|
||||||
|
|
@ -203,7 +218,7 @@ If I forgot to add you, please let me know!
|
||||||
|
|
||||||
[Github issues]: https://github.com/VLSIDA/OpenRAM/issues
|
[Github issues]: https://github.com/VLSIDA/OpenRAM/issues
|
||||||
[Github pull request]: https://github.com/VLSIDA/OpenRAM/pulls
|
[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
|
[documentation]: https://docs.google.com/presentation/d/10InGB33N51I6oBHnqpU7_w9DXlx-qe9zdrlco2Yc5co/edit?usp=sharing
|
||||||
[dev-group]: mailto:openram-dev-group@ucsc.edu
|
[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
|
[dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu
|
||||||
[user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu
|
[user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu
|
||||||
|
|
||||||
|
[Klayout]: https://www.klayout.de/
|
||||||
[Magic]: http://opencircuitdesign.com/magic/
|
[Magic]: http://opencircuitdesign.com/magic/
|
||||||
[Netgen]: http://opencircuitdesign.com/netgen/
|
[Netgen]: http://opencircuitdesign.com/netgen/
|
||||||
[Qflow]: http://opencircuitdesign.com/qflow/history.html
|
[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/
|
[Xyce]: http://xyce.sandia.gov/
|
||||||
[Git]: https://git-scm.com/
|
[Git]: https://git-scm.com/
|
||||||
|
|
||||||
[OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/
|
|
||||||
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
|
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
|
||||||
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
|
[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
|
|
||||||
|
|
|
||||||
|
|
@ -104,4 +104,3 @@ clean_model:
|
||||||
clean:
|
clean:
|
||||||
find . -name \*.pyc -exec rm {} \;
|
find . -name \*.pyc -exec rm {} \;
|
||||||
find . -name \*~ -exec rm {} \;
|
find . -name \*~ -exec rm {} \;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 *
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
import collections
|
import collections
|
||||||
import debug
|
import debug
|
||||||
from tech import drc
|
from tech import drc
|
||||||
from vector import vector
|
from .vector import vector
|
||||||
import design
|
from .design import design
|
||||||
|
|
||||||
|
|
||||||
class channel_net():
|
class channel_net():
|
||||||
|
|
@ -75,7 +75,7 @@ class channel_net():
|
||||||
return min_overlap or max_overlap
|
return min_overlap or max_overlap
|
||||||
|
|
||||||
|
|
||||||
class channel_route(design.design):
|
class channel_route(design):
|
||||||
|
|
||||||
unique_id = 0
|
unique_id = 0
|
||||||
|
|
||||||
|
|
@ -242,12 +242,12 @@ class channel_route(design.design):
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
self.add_vertical_trunk_route(net.pins,
|
self.add_vertical_trunk_route(net.pins,
|
||||||
current_offset,
|
current_offset,
|
||||||
self.vertical_nonpref_pitch)
|
self.horizontal_pitch)
|
||||||
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
|
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
|
||||||
else:
|
else:
|
||||||
self.add_horizontal_trunk_route(net.pins,
|
self.add_horizontal_trunk_route(net.pins,
|
||||||
current_offset,
|
current_offset,
|
||||||
self.horizontal_nonpref_pitch)
|
self.vertical_pitch)
|
||||||
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
|
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
|
||||||
|
|
||||||
# Remove the net from other constriants in the VCG
|
# Remove the net from other constriants in the VCG
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import hierarchy_design
|
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, layer
|
from .hierarchy_design import hierarchy_design
|
||||||
import tech
|
from .vector import vector
|
||||||
from vector import vector
|
from tech import drc, layer, preferred_directions
|
||||||
from sram_factory import factory
|
from tech import layer as tech_layers
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
class contact(hierarchy_design.hierarchy_design):
|
class contact(hierarchy_design):
|
||||||
"""
|
"""
|
||||||
Object for a contact shape with its conductor enclosures. Creates
|
Object for a contact shape with its conductor enclosures. Creates
|
||||||
a contact array minimum active or poly enclosure and metal1
|
a contact array minimum active or poly enclosure and metal1
|
||||||
|
|
@ -51,20 +49,20 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
|
|
||||||
# Non-preferred directions
|
# Non-preferred directions
|
||||||
if directions == "nonpref":
|
if directions == "nonpref":
|
||||||
first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V"
|
first_dir = "H" if preferred_directions[layer_stack[0]]=="V" else "V"
|
||||||
second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V"
|
second_dir = "H" if preferred_directions[layer_stack[2]]=="V" else "V"
|
||||||
self.directions = (first_dir, second_dir)
|
self.directions = (first_dir, second_dir)
|
||||||
# Preferred directions
|
# Preferred directions
|
||||||
elif directions == "pref":
|
elif directions == "pref":
|
||||||
self.directions = (tech.preferred_directions[layer_stack[0]],
|
self.directions = (preferred_directions[layer_stack[0]],
|
||||||
tech.preferred_directions[layer_stack[2]])
|
preferred_directions[layer_stack[2]])
|
||||||
# User directions
|
# User directions
|
||||||
elif directions:
|
elif directions:
|
||||||
self.directions = directions
|
self.directions = directions
|
||||||
# Preferred directions
|
# Preferred directions
|
||||||
else:
|
else:
|
||||||
self.directions = (tech.preferred_directions[layer_stack[0]],
|
self.directions = (preferred_directions[layer_stack[0]],
|
||||||
tech.preferred_directions[layer_stack[2]])
|
preferred_directions[layer_stack[2]])
|
||||||
self.offset = vector(0, 0)
|
self.offset = vector(0, 0)
|
||||||
self.implant_type = implant_type
|
self.implant_type = implant_type
|
||||||
self.well_type = well_type
|
self.well_type = well_type
|
||||||
|
|
@ -101,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
self.second_layer_name = second_layer
|
self.second_layer_name = second_layer
|
||||||
|
|
||||||
# Contacts will have unique per first 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
|
self.via_layer_name = via_layer
|
||||||
elif via_layer == "contact":
|
elif via_layer == "contact":
|
||||||
if first_layer in ("active", "poly"):
|
if first_layer in ("active", "poly"):
|
||||||
|
|
@ -194,7 +192,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
def create_nitride_cut_enclosure(self):
|
def create_nitride_cut_enclosure(self):
|
||||||
""" Special layer that encloses poly contacts in some processes """
|
""" Special layer that encloses poly contacts in some processes """
|
||||||
# Check if there is a special poly nitride cut layer
|
# Check if there is a special poly nitride cut layer
|
||||||
if "npc" not in tech.layer:
|
if "npc" not in tech_layers:
|
||||||
return
|
return
|
||||||
|
|
||||||
npc_enclose_poly = drc("npc_enclose_poly")
|
npc_enclose_poly = drc("npc_enclose_poly")
|
||||||
|
|
@ -256,7 +254,7 @@ class contact(hierarchy_design.hierarchy_design):
|
||||||
|
|
||||||
# Optionally implant well if layer exists
|
# Optionally implant well if layer exists
|
||||||
well_layer = "{}well".format(self.well_type)
|
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)
|
well_width_rule = drc("minwidth_" + well_layer)
|
||||||
self.well_enclose_active = drc(well_layer + "_enclose_active")
|
self.well_enclose_active = drc(well_layer + "_enclose_active")
|
||||||
self.well_width = max(self.first_layer_width + 2 * self.well_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()
|
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,13 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from hierarchy_design import hierarchy_design
|
import debug
|
||||||
import utils
|
|
||||||
import contact
|
|
||||||
from tech import GDS, layer
|
from tech import GDS, layer
|
||||||
from tech import preferred_directions
|
from tech import preferred_directions
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import re
|
from . import utils
|
||||||
import debug
|
from .hierarchy_design import hierarchy_design
|
||||||
|
|
||||||
|
|
||||||
class design(hierarchy_design):
|
class design(hierarchy_design):
|
||||||
|
|
@ -68,207 +66,20 @@ class design(hierarchy_design):
|
||||||
|
|
||||||
self.setup_multiport_constants()
|
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):
|
def check_pins(self):
|
||||||
for pin_name in self.pins:
|
for pin_name in self.pins:
|
||||||
pins = self.get_pins(pin_name)
|
pins = self.get_pins(pin_name)
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
print(pin_name, pin)
|
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):
|
def setup_multiport_constants(self):
|
||||||
"""
|
"""
|
||||||
These are contants and lists that aid multiport design.
|
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)
|
total_module_power += inst.mod.analytical_power(corner, load)
|
||||||
return total_module_power
|
return total_module_power
|
||||||
|
|
||||||
design.setup_drc_constants()
|
|
||||||
design.setup_layer_constants()
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
This provides a set of useful generic types for the gdsMill interface.
|
This provides a set of useful generic types for the gdsMill interface.
|
||||||
"""
|
"""
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from .vector import vector
|
||||||
import tech
|
import tech
|
||||||
import math
|
import math
|
||||||
import copy
|
import copy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from utils import round_to_grid
|
from .utils import round_to_grid
|
||||||
|
|
||||||
|
|
||||||
class geometry:
|
class geometry:
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import hierarchy_layout
|
from .hierarchy_layout import layout
|
||||||
import hierarchy_spice
|
from .hierarchy_spice import spice
|
||||||
import debug
|
import debug
|
||||||
|
import os
|
||||||
from globals import OPTS
|
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.
|
Design Class for all modules to inherit the base features.
|
||||||
Class consisting of a set of modules and instances of these modules
|
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.drc_errors = "skipped"
|
||||||
self.lvs_errors = "skipped"
|
self.lvs_errors = "skipped"
|
||||||
|
|
||||||
hierarchy_spice.spice.__init__(self, name, cell_name)
|
# Flag for library cells which is recomputed in hierachy_layout
|
||||||
hierarchy_layout.layout.__init__(self, name, cell_name)
|
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()
|
self.init_graph_params()
|
||||||
|
|
||||||
def get_layout_pins(self, inst):
|
def get_layout_pins(self, inst):
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -12,10 +12,11 @@ import math
|
||||||
import tech
|
import tech
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from delay_data import delay_data
|
from .delay_data import delay_data
|
||||||
from wire_spice_model import wire_spice_model
|
from .wire_spice_model import wire_spice_model
|
||||||
from power_data import power_data
|
from .power_data import power_data
|
||||||
import logical_effort
|
from .logical_effort import convert_relative_c_to_farad, convert_farad_to_relative_c
|
||||||
|
|
||||||
|
|
||||||
class spice():
|
class spice():
|
||||||
"""
|
"""
|
||||||
|
|
@ -36,19 +37,20 @@ class spice():
|
||||||
# If we have a separate lvs directory, then all the lvs files
|
# If we have a separate lvs directory, then all the lvs files
|
||||||
# should be in there (all or nothing!)
|
# should be in there (all or nothing!)
|
||||||
try:
|
try:
|
||||||
lvs_subdir = tech.lvs_lib
|
from tech import lvs_name
|
||||||
except AttributeError:
|
lvs_dir = OPTS.openram_tech + lvs_name + "_lvs_lib/"
|
||||||
lvs_subdir = "lvs_lib"
|
except ImportError:
|
||||||
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
|
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"
|
||||||
self.lvs_file = lvs_dir + cell_name + ".sp"
|
if not os.path.exists(self.lvs_file):
|
||||||
else:
|
|
||||||
self.lvs_file = self.sp_file
|
self.lvs_file = self.sp_file
|
||||||
|
|
||||||
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
|
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
|
||||||
# Holds subckts/mods for this module
|
# Holds subckts/mods for this module
|
||||||
self.mods = []
|
self.mods = set()
|
||||||
# Holds the pins for this module (in order)
|
# Holds the pins for this module (in order)
|
||||||
self.pins = []
|
self.pins = []
|
||||||
# The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND
|
# The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND
|
||||||
|
|
@ -187,10 +189,6 @@ class spice():
|
||||||
inout_list.append(pin)
|
inout_list.append(pin)
|
||||||
return inout_list
|
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):
|
def connect_inst(self, args, check=True):
|
||||||
"""
|
"""
|
||||||
Connects the pins of the last instance added
|
Connects the pins of the last instance added
|
||||||
|
|
@ -281,7 +279,10 @@ class spice():
|
||||||
# parses line into ports and remove subckt
|
# parses line into ports and remove subckt
|
||||||
lvs_pins = subckt_line.split(" ")[2:]
|
lvs_pins = subckt_line.split(" ")[2:]
|
||||||
debug.check(lvs_pins == self.pins,
|
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):
|
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."""
|
"""Checks if a net name exists in the current. Intended to be check nets in hand-made cells."""
|
||||||
|
|
@ -442,7 +443,7 @@ class spice():
|
||||||
|
|
||||||
# FIXME: Slew is not used in the model right now.
|
# FIXME: Slew is not used in the model right now.
|
||||||
# Can be added heuristically as linear factor
|
# 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)
|
stage_effort = self.get_stage_effort(relative_cap)
|
||||||
|
|
||||||
# If it fails, then keep running with a valid object.
|
# If it fails, then keep running with a valid object.
|
||||||
|
|
@ -510,7 +511,7 @@ class spice():
|
||||||
# Override this function within a module if a more accurate input capacitance is needed.
|
# Override this function within a module if a more accurate input capacitance is needed.
|
||||||
# Input/outputs with differing capacitances is not implemented.
|
# Input/outputs with differing capacitances is not implemented.
|
||||||
relative_cap = self.input_load()
|
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):
|
def input_load(self):
|
||||||
"""Inform users undefined relative capacitance functions used for analytical delays."""
|
"""Inform users undefined relative capacitance functions used for analytical delays."""
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
|
from base import vector
|
||||||
|
from base import pin_layout
|
||||||
from tech import layer_names
|
from tech import layer_names
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from vector import vector
|
|
||||||
from pin_layout import pin_layout
|
|
||||||
|
|
||||||
|
|
||||||
class lef:
|
class lef:
|
||||||
|
|
@ -119,14 +119,13 @@ class lef:
|
||||||
old_blockages = list(self.blockages[pin.layer])
|
old_blockages = list(self.blockages[pin.layer])
|
||||||
for blockage in old_blockages:
|
for blockage in old_blockages:
|
||||||
if blockage.overlaps(inflated_pin):
|
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 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
|
continue
|
||||||
|
|
||||||
# Remove the old blockage and add the new ones
|
# Remove the old blockage and add the new ones
|
||||||
self.blockages[pin.layer].remove(blockage)
|
self.blockages[pin.layer].remove(blockage)
|
||||||
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
|
|
||||||
new_blockages = blockage.cut(intersection_pin)
|
new_blockages = blockage.cut(intersection_pin)
|
||||||
self.blockages[pin.layer].extend(new_blockages)
|
self.blockages[pin.layer].extend(new_blockages)
|
||||||
# We split something so make another pass
|
# We split something so make another pass
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import parameter
|
||||||
|
|
||||||
class logical_effort():
|
class logical_effort():
|
||||||
"""
|
"""
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import GDS, drc
|
from tech import GDS, drc
|
||||||
from vector import vector
|
from .vector import vector
|
||||||
from tech import layer, layer_indices
|
from tech import layer, layer_indices
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
|
@ -174,6 +174,10 @@ class pin_layout:
|
||||||
|
|
||||||
def intersection(self, other):
|
def intersection(self, other):
|
||||||
""" Check if a shape overlaps with a rectangle """
|
""" Check if a shape overlaps with a rectangle """
|
||||||
|
|
||||||
|
if not self.overlaps(other):
|
||||||
|
return None
|
||||||
|
|
||||||
(ll, ur) = self.rect
|
(ll, ur) = self.rect
|
||||||
(oll, our) = other.rect
|
(oll, our) = other.rect
|
||||||
|
|
||||||
|
|
@ -182,7 +186,10 @@ class pin_layout:
|
||||||
min_y = max(ll.y, oll.y)
|
min_y = max(ll.y, oll.y)
|
||||||
max_y = min(ur.y, our.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):
|
def xoverlaps(self, other):
|
||||||
""" Check if shape has x overlap """
|
""" Check if shape has x overlap """
|
||||||
|
|
@ -504,12 +511,19 @@ class pin_layout:
|
||||||
elif other.contains(self):
|
elif other.contains(self):
|
||||||
return math.inf
|
return math.inf
|
||||||
else:
|
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
|
# This is the common case where two pairs of edges overlap
|
||||||
# at two points, so just find the distance between those two points
|
# at two points, so just find the distance between those two points
|
||||||
if len(intersections) == 2:
|
if len(intersections) == 2:
|
||||||
(p1, p2) = intersections
|
(p1, p2) = intersections
|
||||||
return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2))
|
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:
|
else:
|
||||||
# This is where we had a corner intersection or none
|
# This is where we had a corner intersection or none
|
||||||
return 0
|
return 0
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from tech import drc
|
|
||||||
import debug
|
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 itertools import tee
|
||||||
from vector import vector
|
|
||||||
from vector3d import vector3d
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
class route(design):
|
class route(design):
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
|
|
||||||
import gdsMill
|
from gdsMill import gdsMill
|
||||||
import tech
|
import tech
|
||||||
import globals
|
import globals
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from .vector import vector
|
||||||
from pin_layout import pin_layout
|
from .pin_layout import pin_layout
|
||||||
try:
|
try:
|
||||||
from tech import special_purposes
|
from tech import special_purposes
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ class vector():
|
||||||
else:
|
else:
|
||||||
self.x=float(value[0])
|
self.x=float(value[0])
|
||||||
self.y=float(value[1])
|
self.y=float(value[1])
|
||||||
|
self._hash = hash((self.x,self.y))
|
||||||
|
|
||||||
def __getitem__(self, index):
|
def __getitem__(self, index):
|
||||||
"""
|
"""
|
||||||
|
|
@ -104,6 +105,7 @@ class vector():
|
||||||
def snap_to_grid(self):
|
def snap_to_grid(self):
|
||||||
self.x = self.snap_offset_to_grid(self.x)
|
self.x = self.snap_offset_to_grid(self.x)
|
||||||
self.y = self.snap_offset_to_grid(self.y)
|
self.y = self.snap_offset_to_grid(self.y)
|
||||||
|
self._hash = hash((self.x,self.y))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def snap_offset_to_grid(self, offset):
|
def snap_offset_to_grid(self, offset):
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,12 @@ class verilog:
|
||||||
|
|
||||||
self.vf.write("\n")
|
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:
|
for port in self.all_ports:
|
||||||
self.register_inputs(port)
|
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:
|
for port in self.all_ports:
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
self.add_write_block(port)
|
self.add_write_block(port)
|
||||||
|
|
@ -162,7 +162,7 @@ class verilog:
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
self.vf.write(" #(T_HOLD) dout{0} = {1}'bx;\n".format(port, self.word_size))
|
self.vf.write(" #(T_HOLD) dout{0} = {1}'bx;\n".format(port, self.word_size))
|
||||||
if port in self.readwrite_ports:
|
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))
|
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:
|
elif port in self.read_ports:
|
||||||
self.vf.write(" if ( !csb{0}_reg && VERBOSE ) \n".format(port))
|
self.vf.write(" if ( !csb{0}_reg && VERBOSE ) \n".format(port))
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from tech import drc
|
from tech import drc
|
||||||
import contact
|
from .wire_path import wire_path
|
||||||
from wire_path import wire_path
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -69,15 +68,24 @@ class wire(wire_path):
|
||||||
This is contact direction independent pitch,
|
This is contact direction independent pitch,
|
||||||
i.e. we take the maximum contact dimension
|
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
|
(layer1, via, layer2) = layer_stack
|
||||||
|
|
||||||
if layer1 == "poly" or layer1 == "active":
|
if layer1 == "poly" or layer1 == "active":
|
||||||
contact1 = getattr(contact, layer1 + "_contact")
|
try:
|
||||||
|
contact1 = getattr(layout, layer1 + "_contact")
|
||||||
|
except AttributeError:
|
||||||
|
breakpoint()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
contact1 = getattr(contact, layer1 + "_via")
|
contact1 = getattr(layout, layer1 + "_via")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
contact1 = getattr(contact, layer2 + "_via")
|
contact1 = getattr(layout, layer2 + "_via")
|
||||||
max_contact = max(contact1.width, contact1.height)
|
max_contact = max(contact1.width, contact1.height)
|
||||||
|
|
||||||
layer1_space = drc("{0}_to_{0}".format(layer1))
|
layer1_space = drc("{0}_to_{0}".format(layer1))
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
from .vector import vector
|
||||||
|
from .utils import snap_to_grid
|
||||||
|
from .design import design
|
||||||
from tech import drc
|
from tech import drc
|
||||||
from tech import layer as techlayer
|
from tech import layer as techlayer
|
||||||
import debug
|
|
||||||
from vector import vector
|
|
||||||
from utils import snap_to_grid
|
|
||||||
|
|
||||||
def create_rectilinear_route(my_list):
|
def create_rectilinear_route(my_list):
|
||||||
""" Add intermediate nodes if it isn't rectilinear. Also skip
|
""" Add intermediate nodes if it isn't rectilinear. Also skip
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import math
|
||||||
import tech
|
import tech
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import timing_graph
|
from base import timing_graph
|
||||||
|
|
||||||
|
|
||||||
class simulation():
|
class simulation():
|
||||||
|
|
@ -572,7 +572,7 @@ class simulation():
|
||||||
self.sram.graph_exclude_column_mux(self.bitline_column, port)
|
self.sram.graph_exclude_column_mux(self.bitline_column, port)
|
||||||
|
|
||||||
# Generate new graph every analysis as edges might change depending on test bit
|
# 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_instance_name = "X{}".format(self.sram.name)
|
||||||
self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
|
self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from .datasheet_gen import datasheet_gen
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from table_gen import *
|
from .table_gen import *
|
||||||
import os
|
import os
|
||||||
import base64
|
import base64
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ from globals import OPTS
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
import csv
|
import csv
|
||||||
import datasheet
|
from .datasheet import datasheet
|
||||||
import table_gen
|
from .table_gen import table_gen
|
||||||
|
|
||||||
# def process_name(corner):
|
# def process_name(corner):
|
||||||
# """
|
# """
|
||||||
|
|
@ -400,7 +400,7 @@ def parse_characterizer_csv(f, pages):
|
||||||
if found == 0:
|
if found == 0:
|
||||||
|
|
||||||
# if this is the first corner for this sram, run first time configuration and set up tables
|
# 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)
|
pages.append(new_sheet)
|
||||||
|
|
||||||
new_sheet.git_id = ORIGIN_ID
|
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,
|
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]
|
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(
|
new_sheet.corners_table.add_row(
|
||||||
['Transistor Type', 'Power Supply', 'Temperature', 'Corner Name'])
|
['Transistor Type', 'Power Supply', 'Temperature', 'Corner Name'])
|
||||||
new_sheet.corners_table.add_row(
|
new_sheet.corners_table.add_row(
|
||||||
[PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
|
[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")
|
"operating_table")
|
||||||
new_sheet.operating_table.add_row(
|
new_sheet.operating_table.add_row(
|
||||||
['Parameter', 'Min', 'Typ', 'Max', 'Units'])
|
['Parameter', 'Min', 'Typ', 'Max', 'Units'])
|
||||||
|
|
@ -432,10 +432,10 @@ def parse_characterizer_csv(f, pages):
|
||||||
# failed to provide non-zero MIN_PERIOD
|
# failed to provide non-zero MIN_PERIOD
|
||||||
new_sheet.operating_table.add_row(
|
new_sheet.operating_table.add_row(
|
||||||
['Operating Frequency (F)', '', '', "not available in netlist only", 'MHz'])
|
['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(
|
new_sheet.power_table.add_row(
|
||||||
['Pins', 'Mode', 'Power', 'Units'])
|
['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(
|
new_sheet.timing_table.add_row(
|
||||||
['Parameter', 'Min', 'Max', 'Units'])
|
['Parameter', 'Min', 'Max', 'Units'])
|
||||||
# parse initial timing information
|
# parse initial timing information
|
||||||
|
|
@ -592,10 +592,10 @@ def parse_characterizer_csv(f, pages):
|
||||||
else:
|
else:
|
||||||
break
|
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.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'])
|
new_sheet.io_table.add_row(['Type', 'Value'])
|
||||||
|
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class table_gen:
|
class table_gen:
|
||||||
"""small library of functions to generate the html tables"""
|
"""small library of functions to generate the html tables"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 *
|
||||||
|
|
@ -156,6 +156,16 @@ class bitcell(cell):
|
||||||
|
|
||||||
self.storage_nets = storage_nets
|
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():
|
class cell_properties():
|
||||||
"""
|
"""
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from drc_value import *
|
from .drc_value import *
|
||||||
from drc_lut import *
|
from .drc_lut import *
|
||||||
|
|
||||||
|
|
||||||
class design_rules(dict):
|
class design_rules(dict):
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -22,7 +22,7 @@ import getpass
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
VERSION = "1.1.19"
|
VERSION = "1.2.0"
|
||||||
NAME = "OpenRAM v{}".format(VERSION)
|
NAME = "OpenRAM v{}".format(VERSION)
|
||||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||||
|
|
||||||
|
|
@ -252,7 +252,8 @@ def setup_bitcell():
|
||||||
|
|
||||||
# See if bitcell exists
|
# See if bitcell exists
|
||||||
try:
|
try:
|
||||||
__import__(OPTS.bitcell)
|
c = importlib.import_module("modules." + OPTS.bitcell)
|
||||||
|
mod = getattr(c, OPTS.bitcell)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Use the pbitcell if we couldn't find a custom bitcell
|
# Use the pbitcell if we couldn't find a custom bitcell
|
||||||
# or its custom replica bitcell
|
# or its custom replica bitcell
|
||||||
|
|
@ -377,7 +378,6 @@ def read_config(config_file, is_unit_test=True):
|
||||||
ports,
|
ports,
|
||||||
OPTS.tech_name)
|
OPTS.tech_name)
|
||||||
|
|
||||||
|
|
||||||
def end_openram():
|
def end_openram():
|
||||||
""" Clean up openram for a proper exit """
|
""" Clean up openram for a proper exit """
|
||||||
cleanup_paths()
|
cleanup_paths()
|
||||||
|
|
@ -431,19 +431,12 @@ def setup_paths():
|
||||||
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
||||||
except:
|
except:
|
||||||
debug.error("$OPENRAM_HOME is not properly defined.", 1)
|
debug.error("$OPENRAM_HOME is not properly defined.", 1)
|
||||||
|
|
||||||
debug.check(os.path.isdir(OPENRAM_HOME),
|
debug.check(os.path.isdir(OPENRAM_HOME),
|
||||||
"$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME))
|
"$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME))
|
||||||
|
|
||||||
# Add all of the subdirs to the python path
|
if OPENRAM_HOME not in sys.path:
|
||||||
# These subdirs are modules and don't need
|
debug.error("Please add OPENRAM_HOME to the PYTHONPATH.", -1)
|
||||||
# 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))
|
|
||||||
|
|
||||||
# Use a unique temp subdirectory if multithreaded
|
# Use a unique temp subdirectory if multithreaded
|
||||||
if OPTS.num_threads > 1 or OPTS.openram_temp == "/tmp":
|
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__) + "/"
|
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
|
tech_path = OPTS.openram_tech
|
||||||
sys.path.append(tech_path)
|
sys.path.insert(0, tech_path)
|
||||||
try:
|
try:
|
||||||
import tech
|
import tech
|
||||||
except ImportError:
|
except ImportError:
|
||||||
debug.error("Could not load tech module.", -1)
|
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/")
|
custom_mod_path = os.path.join(tech_path, "modules/")
|
||||||
if os.path.exists(custom_mod_path):
|
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):
|
def print_time(name, now_time, last_time=None, indentation=2):
|
||||||
|
|
|
||||||
|
|
@ -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 *
|
||||||
|
|
@ -6,20 +6,20 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import vector
|
||||||
import design
|
from base import design
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
class and2_dec(design.design):
|
class and2_dec(design):
|
||||||
"""
|
"""
|
||||||
This is an AND with configurable drive strength.
|
This is an AND with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
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))
|
debug.info(1, "Creating and2_dec {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
|
|
@ -43,9 +43,6 @@ class and2_dec(design.design):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
size=self.size)
|
size=self.size)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,19 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import design
|
||||||
import design
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
class and3_dec(design.design):
|
class and3_dec(design):
|
||||||
"""
|
"""
|
||||||
This is an AND with configurable drive strength.
|
This is an AND with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
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))
|
debug.info(1, "Creating and3_dec {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
@ -41,9 +41,6 @@ class and3_dec(design.design):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
size=self.size)
|
size=self.size)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
self.route_layer = "li"
|
self.route_layer = "li"
|
||||||
|
|
|
||||||
|
|
@ -6,20 +6,20 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import design
|
||||||
import design
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer
|
from tech import layer
|
||||||
|
|
||||||
|
|
||||||
class and4_dec(design.design):
|
class and4_dec(design):
|
||||||
"""
|
"""
|
||||||
This is an AND with configurable drive strength.
|
This is an AND with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
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))
|
debug.info(1, "Creating and4_dec {}".format(name))
|
||||||
self.add_comment("size: {}".format(size))
|
self.add_comment("size: {}".format(size))
|
||||||
|
|
@ -43,9 +43,6 @@ class and4_dec(design.design):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
size=self.size)
|
size=self.size)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
if "li" in layer:
|
if "li" in layer:
|
||||||
self.route_layer = "li"
|
self.route_layer = "li"
|
||||||
|
|
@ -129,4 +126,3 @@ class and4_dec(design.design):
|
||||||
offset=pin.center(),
|
offset=pin.center(),
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,16 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from math import log, ceil, floor
|
from math import log, ceil, floor
|
||||||
from tech import drc
|
from tech import drc
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer_properties as layer_props
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class bank(design.design):
|
class bank(design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated a single bank including bitcell array,
|
Dynamically generated a single bank including bitcell array,
|
||||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||||
|
|
@ -229,7 +229,7 @@ class bank(design.design):
|
||||||
|
|
||||||
# UPPER LEFT QUADRANT
|
# UPPER LEFT QUADRANT
|
||||||
# To the left of the bitcell array above the predecoders and control logic
|
# 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.port_address_offsets[port] = vector(-x_offset,
|
||||||
self.main_bitcell_array_bottom)
|
self.main_bitcell_array_bottom)
|
||||||
|
|
||||||
|
|
@ -272,7 +272,7 @@ class bank(design.design):
|
||||||
|
|
||||||
# LOWER RIGHT QUADRANT
|
# LOWER RIGHT QUADRANT
|
||||||
# To the right of the bitcell array
|
# 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.port_address_offsets[port] = vector(x_offset,
|
||||||
self.main_bitcell_array_bottom)
|
self.main_bitcell_array_bottom)
|
||||||
|
|
||||||
|
|
@ -364,9 +364,9 @@ class bank(design.design):
|
||||||
self.num_col_addr_lines = 0
|
self.num_col_addr_lines = 0
|
||||||
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
|
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
|
||||||
|
|
||||||
# A space for wells or jogging m2
|
# Gap between decoder and array
|
||||||
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||||
3 * self.m2_pitch,
|
2 * self.m2_pitch,
|
||||||
drc("nwell_to_nwell"))
|
drc("nwell_to_nwell"))
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
|
|
@ -389,7 +389,6 @@ class bank(design.design):
|
||||||
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||||
cols=self.num_cols + self.num_spare_cols,
|
cols=self.num_cols + self.num_spare_cols,
|
||||||
rows=self.num_rows)
|
rows=self.num_rows)
|
||||||
self.add_mod(self.bitcell_array)
|
|
||||||
|
|
||||||
self.port_address = []
|
self.port_address = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -397,21 +396,17 @@ class bank(design.design):
|
||||||
cols=self.num_cols + self.num_spare_cols,
|
cols=self.num_cols + self.num_spare_cols,
|
||||||
rows=self.num_rows,
|
rows=self.num_rows,
|
||||||
port=port))
|
port=port))
|
||||||
self.add_mod(self.port_address[port])
|
|
||||||
|
|
||||||
self.port_data = []
|
self.port_data = []
|
||||||
self.bit_offsets = self.get_column_offsets()
|
self.bit_offsets = self.get_column_offsets()
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
temp_pre = factory.create(module_type="port_data",
|
self.port_data.append(factory.create(module_type="port_data",
|
||||||
sram_config=self.sram_config,
|
sram_config=self.sram_config,
|
||||||
port=port,
|
port=port,
|
||||||
bit_offsets=self.bit_offsets)
|
bit_offsets=self.bit_offsets))
|
||||||
self.port_data.append(temp_pre)
|
|
||||||
self.add_mod(self.port_data[port])
|
|
||||||
|
|
||||||
if(self.num_banks > 1):
|
if(self.num_banks > 1):
|
||||||
self.bank_select = factory.create(module_type="bank_select")
|
self.bank_select = factory.create(module_type="bank_select")
|
||||||
self.add_mod(self.bank_select)
|
|
||||||
|
|
||||||
def create_bitcell_array(self):
|
def create_bitcell_array(self):
|
||||||
""" Creating Bitcell Array """
|
""" Creating Bitcell Array """
|
||||||
|
|
@ -528,26 +523,9 @@ class bank(design.design):
|
||||||
|
|
||||||
if self.col_addr_size == 0:
|
if self.col_addr_size == 0:
|
||||||
return
|
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:
|
else:
|
||||||
# No error checking before?
|
self.column_decoder = factory.create(module_type="column_decoder",
|
||||||
debug.error("Invalid column decoder?", -1)
|
col_addr_size=self.col_addr_size)
|
||||||
self.add_mod(self.column_decoder)
|
|
||||||
|
|
||||||
self.column_decoder_inst = [None] * len(self.all_ports)
|
self.column_decoder_inst = [None] * len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -611,15 +589,22 @@ class bank(design.design):
|
||||||
|
|
||||||
def route_supplies(self):
|
def route_supplies(self):
|
||||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||||
|
|
||||||
# Copy only the power pins already on the power layer
|
# Copy only the power pins already on the power layer
|
||||||
# (this won't add vias to internal bitcell pins, for example)
|
# (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)
|
# This avoids getting copy errors on vias and other instances
|
||||||
self.copy_power_pins(inst, "gnd", add_vias=False)
|
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:
|
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']):
|
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.
|
# If we use the pinvbuf as the decoder, we need to add power pins.
|
||||||
# Other decoders already have them.
|
# Other decoders already have them.
|
||||||
|
|
@ -926,23 +911,14 @@ class bank(design.design):
|
||||||
stack = getattr(self, layer_props.bank.stack)
|
stack = getattr(self, layer_props.bank.stack)
|
||||||
pitch = getattr(self, layer_props.bank.pitch)
|
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]
|
for i in range(self.col_addr_size):
|
||||||
decode_names = ["Zb", "Z"]
|
decoder_name = "in_{}".format(i)
|
||||||
|
addr_name = "addr{0}_{1}".format(port, i)
|
||||||
# The Address LSB
|
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||||
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)
|
|
||||||
|
|
||||||
if port % 2:
|
if port % 2:
|
||||||
offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0)
|
offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0)
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import sys
|
from tech import drc
|
||||||
from tech import drc, parameter
|
from base import design
|
||||||
import debug
|
from base import vector
|
||||||
import design
|
from pgates import pinv
|
||||||
import contact
|
from pgates import pnand2
|
||||||
from pinv import pinv
|
from pgates import pnor2
|
||||||
from pnand2 import pnand2
|
|
||||||
from pnor2 import pnor2
|
|
||||||
from vector import vector
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
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
|
"""Create a bank select signal that is combined with an array of
|
||||||
NOR+INV gates to gate the control signals in case of multiple
|
NOR+INV gates to gate the control signals in case of multiple
|
||||||
banks are created in upper level SRAM module
|
banks are created in upper level SRAM module
|
||||||
|
|
@ -78,20 +75,15 @@ class bank_select(design.design):
|
||||||
|
|
||||||
# 1x Inverter
|
# 1x Inverter
|
||||||
self.inv_sel = factory.create(module_type="pinv", height=height)
|
self.inv_sel = factory.create(module_type="pinv", height=height)
|
||||||
self.add_mod(self.inv_sel)
|
|
||||||
|
|
||||||
# 4x Inverter
|
# 4x Inverter
|
||||||
self.inv4x = factory.create(module_type="pinv", height=height, size=4)
|
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.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.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.nand2 = factory.create(module_type="pnand2", height=height)
|
||||||
self.add_mod(self.nand2)
|
|
||||||
|
|
||||||
def calculate_module_offsets(self):
|
def calculate_module_offsets(self):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import cell_properties as props
|
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
|
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
|
single memory cell used in the design. It is a hand-made cell, so
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import cell_properties as props
|
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
|
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
|
single memory cell used in the design. It is a hand-made cell, so
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from bitcell_base_array import bitcell_base_array
|
from .bitcell_base_array import bitcell_base_array
|
||||||
from tech import drc, spice
|
from tech import drc, spice
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array):
|
||||||
|
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
self.route_supplies()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -53,7 +55,6 @@ class bitcell_array(bitcell_base_array):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
self.add_mod(self.cell)
|
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
|
|
|
||||||
|
|
@ -7,18 +7,18 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import logical_effort
|
from base import logical_effort
|
||||||
from tech import parameter, drc, layer, spice
|
from tech import parameter, drc, layer, spice
|
||||||
|
|
||||||
|
|
||||||
class bitcell_base(design.design):
|
class bitcell_base(design):
|
||||||
"""
|
"""
|
||||||
Base bitcell parameters to be over-riden.
|
Base bitcell parameters to be over-riden.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, cell_name=None, prop=None):
|
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
|
# Set the bitcell specific properties
|
||||||
if prop:
|
if prop:
|
||||||
|
|
@ -37,12 +37,12 @@ class bitcell_base(design.design):
|
||||||
# min size NMOS gate load
|
# min size NMOS gate load
|
||||||
read_port_load = 0.5
|
read_port_load = 0.5
|
||||||
|
|
||||||
return logical_effort.logical_effort('bitline',
|
return logical_effort('bitline',
|
||||||
size,
|
size,
|
||||||
cin,
|
cin,
|
||||||
load + read_port_load,
|
load + read_port_load,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
False)
|
False)
|
||||||
|
|
||||||
def analytical_power(self, corner, load):
|
def analytical_power(self, corner, load):
|
||||||
"""Bitcell power in nW. Only characterizes leakage."""
|
"""Bitcell power in nW. Only characterizes leakage."""
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class bitcell_base_array(design.design):
|
class bitcell_base_array(design):
|
||||||
"""
|
"""
|
||||||
Abstract base class for bitcell-arrays -- bitcell, dummy, replica
|
Abstract base class for bitcell-arrays -- bitcell, dummy, replica
|
||||||
"""
|
"""
|
||||||
|
|
@ -43,11 +43,11 @@ class bitcell_base_array(design.design):
|
||||||
# Make a flat list too
|
# Make a flat list too
|
||||||
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
|
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:
|
if row_size == None:
|
||||||
row_size = self.row_size
|
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:
|
for port in self.all_ports:
|
||||||
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
|
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
|
||||||
|
|
||||||
|
|
@ -156,18 +156,15 @@ class bitcell_base_array(design.design):
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=wl_pin.height())
|
height=wl_pin.height())
|
||||||
|
|
||||||
def add_supply_pins(self):
|
def route_supplies(self):
|
||||||
for row in range(self.row_size):
|
for inst in self.cell_inst.values():
|
||||||
for col in range(self.column_size):
|
for pin_name in ["vdd", "gnd"]:
|
||||||
inst = self.cell_inst[row, col]
|
self.copy_layout_pin(inst, pin_name)
|
||||||
for pin_name in ["vdd", "gnd"]:
|
|
||||||
self.copy_layout_pin(inst, pin_name)
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
""" Add the layout pins """
|
""" Add the layout pins """
|
||||||
self.add_bitline_pins()
|
self.add_bitline_pins()
|
||||||
self.add_wl_pins()
|
self.add_wl_pins()
|
||||||
self.add_supply_pins()
|
|
||||||
|
|
||||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||||
tempx = xoffset
|
tempx = xoffset
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# Copyright (c) 2016-2021 Regents of the University of California
|
# Copyright (c) 2016-2021 Regents of the University of California
|
||||||
# All rights reserved.
|
# 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 sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
@ -49,7 +49,6 @@ class col_cap_array(bitcell_base_array):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||||
self.add_mod(self.dummy_cell)
|
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" 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]
|
inst = self.cell_inst[row, col]
|
||||||
for pin_name in ["vdd", "gnd"]:
|
for pin_name in ["vdd", "gnd"]:
|
||||||
for pin in inst.get_pins(pin_name):
|
for pin in inst.get_pins(pin_name):
|
||||||
self.copy_power_pin(pin)
|
self.copy_layout_pin(inst, pin_name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,16 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import cell_properties as props
|
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.
|
Column end cap cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name="col_cap_bitcell_1port"):
|
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")
|
debug.info(2, "Create col_cap bitcell 1 port object")
|
||||||
|
|
||||||
self.no_instances = True
|
self.no_instances = True
|
||||||
|
|
@ -7,16 +7,16 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import cell_properties as props
|
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.
|
Column end cap cell.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name="col_cap_bitcell_2port"):
|
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")
|
debug.info(2, "Create col_cap bitcell 2 port object")
|
||||||
|
|
||||||
self.no_instances = True
|
self.no_instances = True
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -5,16 +5,17 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import pgate
|
from .pgate import *
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, layer
|
from tech import drc, layer
|
||||||
from vector import vector
|
from base import vector
|
||||||
|
from .pgate import *
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from tech import cell_properties as cell_props
|
from tech import cell_properties as cell_props
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class column_mux(pgate.pgate):
|
class column_mux(pgate):
|
||||||
"""
|
"""
|
||||||
This module implements the columnmux bitline cell used in the design.
|
This module implements the columnmux bitline cell used in the design.
|
||||||
Creates a single column mux cell with the given integer size relative
|
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.ptx_width = self.tx_size * drc("minwidth_tx")
|
||||||
self.nmos = factory.create(module_type="ptx",
|
self.nmos = factory.create(module_type="ptx",
|
||||||
width=self.ptx_width)
|
width=self.ptx_width)
|
||||||
self.add_mod(self.nmos)
|
|
||||||
|
|
||||||
# Space it in the center
|
# Space it in the center
|
||||||
self.nmos_lower = self.add_inst(name="mux_tx1",
|
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,
|
self.add_via_center(layers=self.col_mux_stack,
|
||||||
offset=active_pos)
|
offset=active_pos)
|
||||||
|
|
||||||
# Add the M1->..->power_grid_layer stack
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
self.add_power_pin(name="gnd",
|
layer="m1",
|
||||||
loc=active_pos,
|
offset=active_pos)
|
||||||
start_layer="m1")
|
|
||||||
|
|
||||||
# Add well enclosure over all the tx and contact
|
# Add well enclosure over all the tx and contact
|
||||||
if "pwell" in layer:
|
if "pwell" in layer:
|
||||||
|
|
@ -5,16 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
import debug
|
import debug
|
||||||
from tech import layer, preferred_directions
|
from tech import layer, preferred_directions
|
||||||
from vector import vector
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer_properties as layer_props
|
from tech import layer_properties as layer_props
|
||||||
|
|
||||||
|
|
||||||
class column_mux_array(design.design):
|
class column_mux_array(design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated column mux array.
|
Dynamically generated column mux array.
|
||||||
Array of column mux to read the bitlines through the 6T.
|
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",
|
self.mux = factory.create(module_type="column_mux",
|
||||||
bitcell_bl=self.bitcell_bl,
|
bitcell_bl=self.bitcell_bl,
|
||||||
bitcell_br=self.bitcell_br)
|
bitcell_br=self.bitcell_br)
|
||||||
self.add_mod(self.mux)
|
|
||||||
|
|
||||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
|
|
||||||
|
|
@ -148,13 +147,14 @@ class column_mux_array(design.design):
|
||||||
offset=offset,
|
offset=offset,
|
||||||
height=self.height - offset.y)
|
height=self.height - offset.y)
|
||||||
|
|
||||||
for inst in self.mux_inst:
|
def route_supplies(self):
|
||||||
self.copy_layout_pin(inst, "gnd")
|
self.route_horizontal_pins("gnd", self.insts)
|
||||||
|
|
||||||
def add_routing(self):
|
def add_routing(self):
|
||||||
self.add_horizontal_input_rail()
|
self.add_horizontal_input_rail()
|
||||||
self.add_vertical_poly_rail()
|
self.add_vertical_poly_rail()
|
||||||
self.route_bitlines()
|
self.route_bitlines()
|
||||||
|
self.route_supplies()
|
||||||
|
|
||||||
def add_horizontal_input_rail(self):
|
def add_horizontal_input_rail(self):
|
||||||
""" Create address input rails below the mux transistors """
|
""" 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
|
sel_index = col % self.words_per_row
|
||||||
# Add the column x offset to find the right select bit
|
# Add the column x offset to find the right select bit
|
||||||
gate_offset = self.mux_inst[col].get_pin("sel").bc()
|
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
|
# use the y offset from the sel pin and the x offset from the gate
|
||||||
|
|
||||||
offset = vector(gate_offset.x,
|
offset = vector(gate_offset.x,
|
||||||
self.get_pin("sel_{}".format(sel_index)).cy())
|
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",
|
self.add_via_stack_center(from_layer="poly",
|
||||||
to_layer=self.sel_layer,
|
to_layer=self.sel_layer,
|
||||||
offset=offset,
|
offset=bl_offset,
|
||||||
directions=self.via_directions)
|
directions=self.via_directions)
|
||||||
self.add_path("poly", [offset, gate_offset])
|
self.add_path("poly", [offset, gate_offset, bl_offset])
|
||||||
|
|
||||||
def route_bitlines(self):
|
def route_bitlines(self):
|
||||||
""" Connect the output bit-lines to form the appropriate width mux """
|
""" Connect the output bit-lines to form the appropriate width mux """
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
import debug
|
import debug
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import math
|
import math
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
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.
|
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.num_words = num_rows * words_per_row
|
||||||
|
|
||||||
self.enable_delay_chain_resizing = False
|
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.
|
# Determines how much larger the sen delay should be. Accounts for possible error in model.
|
||||||
# FIXME: This should be made a parameter
|
# FIXME: This should be made a parameter
|
||||||
|
|
@ -91,17 +91,13 @@ class control_logic(design.design):
|
||||||
rows=self.num_control_signals,
|
rows=self.num_control_signals,
|
||||||
columns=1)
|
columns=1)
|
||||||
|
|
||||||
self.add_mod(self.ctrl_dff_array)
|
|
||||||
|
|
||||||
self.and2 = factory.create(module_type="pand2",
|
self.and2 = factory.create(module_type="pand2",
|
||||||
size=12,
|
size=12,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.and2)
|
|
||||||
|
|
||||||
self.rbl_driver = factory.create(module_type="pbuf",
|
self.rbl_driver = factory.create(module_type="pbuf",
|
||||||
size=self.num_cols,
|
size=self.num_cols,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.rbl_driver)
|
|
||||||
|
|
||||||
# clk_buf drives a flop for every address
|
# clk_buf drives a flop for every address
|
||||||
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
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,
|
fanout=clock_fanout,
|
||||||
height=dff_height)
|
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
|
# We will use the maximum since this same value is used to size the wl_en
|
||||||
# and the p_en_bar drivers
|
# and the p_en_bar drivers
|
||||||
# max_fanout = max(self.num_rows, self.num_cols)
|
# 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",
|
self.wl_en_driver = factory.create(module_type="pdriver",
|
||||||
size_list=size_list,
|
size_list=size_list,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.wl_en_driver)
|
|
||||||
|
|
||||||
# w_en drives every write driver
|
# w_en drives every write driver
|
||||||
self.wen_and = factory.create(module_type="pand3",
|
self.wen_and = factory.create(module_type="pand3",
|
||||||
size=self.word_size + 8,
|
size=self.word_size + 8,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.wen_and)
|
|
||||||
|
|
||||||
# s_en drives every sense amp
|
# s_en drives every sense amp
|
||||||
self.sen_and3 = factory.create(module_type="pand3",
|
self.sen_and3 = factory.create(module_type="pand3",
|
||||||
size=self.word_size + self.num_spare_cols,
|
size=self.word_size + self.num_spare_cols,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.sen_and3)
|
|
||||||
|
|
||||||
# used to generate inverted signals with low fanout
|
# used to generate inverted signals with low fanout
|
||||||
self.inv = factory.create(module_type="pinv",
|
self.inv = factory.create(module_type="pinv",
|
||||||
size=1,
|
size=1,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
# p_en_bar drives every column in the bitcell array
|
# p_en_bar drives every column in the bitcell array
|
||||||
# but it is sized the same as the wl_en driver with
|
# 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",
|
self.p_en_bar_driver = factory.create(module_type="pdriver",
|
||||||
fanout=self.num_cols,
|
fanout=self.num_cols,
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.p_en_bar_driver)
|
|
||||||
|
|
||||||
self.nand2 = factory.create(module_type="pnand2",
|
self.nand2 = factory.create(module_type="pnand2",
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.nand2)
|
|
||||||
|
|
||||||
debug.check(OPTS.delay_chain_stages % 2,
|
debug.check(OPTS.delay_chain_stages % 2,
|
||||||
"Must use odd number of delay chain stages for inverting delay chain.")
|
"Must use odd number of delay chain stages for inverting delay chain.")
|
||||||
self.delay_chain=factory.create(module_type="delay_chain",
|
self.delay_chain=factory.create(module_type="delay_chain",
|
||||||
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
|
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):
|
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"""
|
"""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):
|
def route_rails(self):
|
||||||
""" Add the input signal inverted tracks """
|
""" Add the input signal inverted tracks """
|
||||||
height = self.control_logic_center.y - self.m2_pitch
|
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",
|
self.input_bus = self.create_vertical_bus("m2",
|
||||||
offset,
|
offset,
|
||||||
|
|
@ -325,7 +313,8 @@ class control_logic(design.design):
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
|
|
||||||
# All of the control logic is placed to the right of the DFFs and bus
|
# 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
|
row = 0
|
||||||
# Add the logic on the right of the bus
|
# Add the logic on the right of the bus
|
||||||
|
|
@ -381,7 +370,7 @@ class control_logic(design.design):
|
||||||
self.route_clk_buf()
|
self.route_clk_buf()
|
||||||
self.route_gated_clk_bar()
|
self.route_gated_clk_bar()
|
||||||
self.route_gated_clk_buf()
|
self.route_gated_clk_buf()
|
||||||
self.route_supply()
|
self.route_supplies()
|
||||||
|
|
||||||
def create_delay(self):
|
def create_delay(self):
|
||||||
""" Create the replica bitline """
|
""" Create the replica bitline """
|
||||||
|
|
@ -720,28 +709,74 @@ class control_logic(design.design):
|
||||||
start=out_pos,
|
start=out_pos,
|
||||||
end=right_pos)
|
end=right_pos)
|
||||||
|
|
||||||
def route_supply(self):
|
def route_supplies(self):
|
||||||
""" Add vdd and gnd to the instance cells """
|
""" 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])
|
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:
|
for inst in self.row_end_inst:
|
||||||
pins = inst.get_pins("vdd")
|
pins = inst.get_pins("vdd")
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
if pin.layer == supply_layer:
|
if pin.layer == pin_layer:
|
||||||
row_loc = pin.rc()
|
row_loc = pin.rc()
|
||||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
||||||
self.add_power_pin("vdd", pin_loc, start_layer=pin.layer)
|
vdd_pin_locs.append(pin_loc)
|
||||||
self.add_path(supply_layer, [row_loc, 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")
|
pins = inst.get_pins("gnd")
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
if pin.layer == supply_layer:
|
if pin.layer == pin_layer:
|
||||||
row_loc = pin.rc()
|
row_loc = pin.rc()
|
||||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
pin_loc = vector(min_row_x_loc, pin.rc().y)
|
||||||
self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
|
gnd_pin_locs.append(pin_loc)
|
||||||
self.add_path(supply_layer, [row_loc, 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, "gnd")
|
||||||
self.copy_layout_pin(self.delay_inst, "vdd")
|
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
|
# Connect this at the bottom of the buffer
|
||||||
out_pin = inst.get_pin("Z")
|
out_pin = inst.get_pin("Z")
|
||||||
out_pos = out_pin.center()
|
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)
|
mid2 = vector(self.input_bus[name].cx(), mid1.y)
|
||||||
bus_pos = self.input_bus[name].center()
|
bus_pos = self.input_bus[name].center()
|
||||||
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
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.
|
Generate a delay chain with the given number of stages and fanout.
|
||||||
Input is a list contains the electrical effort (fanout) of each stage.
|
Input is a list contains the electrical effort (fanout) of each stage.
|
||||||
|
|
@ -69,7 +69,6 @@ class delay_chain(design.design):
|
||||||
|
|
||||||
self.inv = factory.create(module_type="pinv",
|
self.inv = factory.create(module_type="pinv",
|
||||||
height=dff_height)
|
height=dff_height)
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_inverters(self):
|
def create_inverters(self):
|
||||||
""" Create the inverters and connect them based on the stage list """
|
""" 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()])
|
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
|
||||||
|
|
||||||
def route_supplies(self):
|
def route_supplies(self):
|
||||||
# Add power and ground to all the cells except:
|
# These pins get routed in one cell from the left/right
|
||||||
# the fanout driver, the right-most load
|
# because the input signal gets routed on M3 and can interfere with the delay input.
|
||||||
# The routing to connect the loads is over the first and last cells
|
self.route_vertical_pins("vdd", self.driver_inst_list, xside="rx")
|
||||||
# We have an even number of drivers and must only do every other
|
right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list]
|
||||||
# supply rail
|
self.route_vertical_pins("gnd", right_load_insts, xside="lx")
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
|
|
@ -213,4 +203,3 @@ class delay_chain(design.design):
|
||||||
self.add_layout_pin_rect_center(text="out",
|
self.add_layout_pin_rect_center(text="out",
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=a_pin.center())
|
offset=a_pin.center())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from tech import spice
|
from tech import spice
|
||||||
|
|
||||||
|
|
||||||
class dff(design.design):
|
class dff(design):
|
||||||
"""
|
"""
|
||||||
Memory address flip-flop
|
Memory address flip-flop
|
||||||
"""
|
"""
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from vector import vector
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
class dff_array(design.design):
|
class dff_array(design):
|
||||||
"""
|
"""
|
||||||
This is a simple row (or multiple rows) of flops.
|
This is a simple row (or multiple rows) of flops.
|
||||||
Unlike the data flops, these are never spaced out.
|
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.height = self.rows * self.dff.height
|
||||||
|
|
||||||
self.place_dff_array()
|
self.place_dff_array()
|
||||||
|
self.route_supplies()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = factory.create(module_type="dff")
|
self.dff = factory.create(module_type="dff")
|
||||||
self.add_mod(self.dff)
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
|
|
@ -107,17 +107,26 @@ class dff_array(design.design):
|
||||||
|
|
||||||
return dout_name
|
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):
|
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 row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
din_pin = self.dff_insts[row, col].get_pin("D")
|
din_pin = self.dff_insts[row, col].get_pin("D")
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from tech import layer
|
from tech import layer
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class dff_buf(design.design):
|
class dff_buf(design):
|
||||||
"""
|
"""
|
||||||
This is a simple buffered DFF. The output is buffered
|
This is a simple buffered DFF. The output is buffered
|
||||||
with two inverters, of variable size, to provide q
|
with two inverters, of variable size, to provide q
|
||||||
|
|
@ -58,17 +58,14 @@ class dff_buf(design.design):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = factory.create(module_type="dff")
|
self.dff = factory.create(module_type="dff")
|
||||||
self.add_mod(self.dff)
|
|
||||||
|
|
||||||
self.inv1 = factory.create(module_type="pinv",
|
self.inv1 = factory.create(module_type="pinv",
|
||||||
size=self.inv1_size,
|
size=self.inv1_size,
|
||||||
height=self.dff.height)
|
height=self.dff.height)
|
||||||
self.add_mod(self.inv1)
|
|
||||||
|
|
||||||
self.inv2 = factory.create(module_type="pinv",
|
self.inv2 = factory.create(module_type="pinv",
|
||||||
size=self.inv2_size,
|
size=self.inv2_size,
|
||||||
height=self.dff.height)
|
height=self.dff.height)
|
||||||
self.add_mod(self.inv2)
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin_list(["D", "Q", "Qb", "clk", "vdd", "gnd"],
|
self.add_pin_list(["D", "Q", "Qb", "clk", "vdd", "gnd"],
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
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.
|
This is a simple row (or multiple rows) of flops.
|
||||||
Unlike the data flops, these are never spaced out.
|
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",
|
self.dff = factory.create(module_type="dff_buf",
|
||||||
inv1_size=self.inv1_size,
|
inv1_size=self.inv1_size,
|
||||||
inv2_size=self.inv2_size)
|
inv2_size=self.inv2_size)
|
||||||
self.add_mod(self.dff)
|
|
||||||
|
|
||||||
def create_dff_array(self):
|
def create_dff_array(self):
|
||||||
self.dff_insts={}
|
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")
|
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())
|
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
|
||||||
|
|
||||||
for row in range(self.rows):
|
if self.rows > 1:
|
||||||
for col in range(self.columns):
|
# Vertical straps on ends if multiple rows
|
||||||
# Continous vdd rail along with label.
|
left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)]
|
||||||
vdd_pin=self.dff_insts[row, col].get_pin("vdd")
|
right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)]
|
||||||
self.copy_power_pin(vdd_pin, loc=vdd_pin.lc())
|
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):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,13 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from tech import drc
|
from base import vector
|
||||||
from math import log
|
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS
|
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
|
This is a simple DFF with an inverted output. Some DFFs
|
||||||
do not have Qbar, so this will create it.
|
do not have Qbar, so this will create it.
|
||||||
|
|
@ -66,12 +65,10 @@ class dff_inv(design.design):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = dff_inv.dff_inv(self.inv_size)
|
self.dff = dff_inv.dff_inv(self.inv_size)
|
||||||
self.add_mod(self.dff)
|
|
||||||
|
|
||||||
self.inv1 = factory.create(module_type="pinv",
|
self.inv1 = factory.create(module_type="pinv",
|
||||||
size=self.inv_size,
|
size=self.inv_size,
|
||||||
height=self.dff.height)
|
height=self.dff.height)
|
||||||
self.add_mod(self.inv1)
|
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
self.dff_inst=self.add_inst(name="dff_inv_dff",
|
self.dff_inst=self.add_inst(name="dff_inv_dff",
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
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.
|
This is a simple row (or multiple rows) of flops.
|
||||||
Unlike the data flops, these are never spaced out.
|
Unlike the data flops, these are never spaced out.
|
||||||
|
|
@ -53,7 +53,6 @@ class dff_inv_array(design.design):
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.dff = factory.create(module_type="dff")
|
self.dff = factory.create(module_type="dff")
|
||||||
self.add_mod(self.dff)
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
|
|
@ -129,11 +128,11 @@ class dff_inv_array(design.design):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
# Adds power pin on left of row
|
# Adds power pin on left of row
|
||||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
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
|
# Adds gnd pin on left of row
|
||||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
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 row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# Copyright (c) 2016-2021 Regents of the University of California
|
# Copyright (c) 2016-2021 Regents of the University of California
|
||||||
# All rights reserved.
|
# 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 sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
@ -36,6 +36,8 @@ class dummy_array(bitcell_base_array):
|
||||||
|
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
self.route_supplies()
|
||||||
|
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -45,7 +47,6 @@ class dummy_array(bitcell_base_array):
|
||||||
|
|
||||||
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
|
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
|
||||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
self.add_mod(self.dummy_cell)
|
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
|
|
@ -99,6 +100,8 @@ class dummy_array(bitcell_base_array):
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=wl_pin.height())
|
height=wl_pin.height())
|
||||||
|
|
||||||
|
def route_supplies(self):
|
||||||
|
|
||||||
# Copy a vdd/gnd layout pin from every cell
|
# Copy a vdd/gnd layout pin from every cell
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import cell_properties as props
|
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
|
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
|
single memory cell used in the design. It is a hand-made cell, so
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from tech import cell_properties as props
|
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.
|
A single bit cell which is forced to store a 0.
|
||||||
This module implements the single memory cell used in the design. It
|
This module implements the single memory cell used in the design. It
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class dummy_pbitcell(design.design):
|
class dummy_pbitcell(design):
|
||||||
"""
|
"""
|
||||||
Creates a replica bitcell using pbitcell
|
Creates a replica bitcell using pbitcell
|
||||||
"""
|
"""
|
||||||
|
|
@ -23,7 +23,7 @@ class dummy_pbitcell(design.design):
|
||||||
self.num_r_ports = OPTS.num_r_ports
|
self.num_r_ports = OPTS.num_r_ports
|
||||||
self.total_ports = self.num_rw_ports + self.num_w_ports + self.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,
|
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_w_ports,
|
||||||
self.num_r_ports))
|
self.num_r_ports))
|
||||||
|
|
@ -56,7 +56,6 @@ class dummy_pbitcell(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.prbc = factory.create(module_type="pbitcell",
|
self.prbc = factory.create(module_type="pbitcell",
|
||||||
dummy_bitcell=True)
|
dummy_bitcell=True)
|
||||||
self.add_mod(self.prbc)
|
|
||||||
|
|
||||||
self.height = self.prbc.height
|
self.height = self.prbc.height
|
||||||
self.width = self.prbc.width
|
self.width = self.prbc.width
|
||||||
|
|
@ -5,16 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import bitcell_base_array
|
from .bitcell_base_array import bitcell_base_array
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from base import vector
|
||||||
import debug
|
import debug
|
||||||
from numpy import cumsum
|
from numpy import cumsum
|
||||||
from tech import layer_properties as layer_props
|
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.
|
Creates a global bitcell array.
|
||||||
Rows is an integer number for all local arrays.
|
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,
|
rbl=self.rbl,
|
||||||
left_rbl=[0],
|
left_rbl=[0],
|
||||||
right_rbl=[1] if len(self.all_ports) > 1 else [])
|
right_rbl=[1] if len(self.all_ports) > 1 else [])
|
||||||
self.add_mod(la)
|
|
||||||
self.local_mods.append(la)
|
self.local_mods.append(la)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -90,7 +89,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
cols=cols,
|
cols=cols,
|
||||||
rbl=self.rbl)
|
rbl=self.rbl)
|
||||||
|
|
||||||
self.add_mod(la)
|
|
||||||
self.local_mods.append(la)
|
self.local_mods.append(la)
|
||||||
|
|
||||||
def add_pins(self):
|
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"""
|
"""Exclude dffs from graph as they do not represent critical path"""
|
||||||
|
|
||||||
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,17 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
import math
|
import math
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer_indices
|
from tech import layer_indices
|
||||||
from tech import layer_stacks
|
from tech import layer_stacks
|
||||||
from tech import layer_properties as layer_props
|
from tech import layer_properties as layer_props
|
||||||
from tech import drc
|
from tech import drc
|
||||||
|
|
||||||
class hierarchical_decoder(design.design):
|
class hierarchical_decoder(design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated hierarchical decoder.
|
Dynamically generated hierarchical decoder.
|
||||||
"""
|
"""
|
||||||
|
|
@ -57,11 +57,12 @@ class hierarchical_decoder(design.design):
|
||||||
self.route_inputs()
|
self.route_inputs()
|
||||||
self.route_outputs()
|
self.route_outputs()
|
||||||
self.route_decoder_bus()
|
self.route_decoder_bus()
|
||||||
self.route_vdd_gnd()
|
|
||||||
|
|
||||||
self.offset_x_coordinates()
|
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.add_boundary()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -69,14 +70,11 @@ class hierarchical_decoder(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.and2 = factory.create(module_type="and2_dec",
|
self.and2 = factory.create(module_type="and2_dec",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and2)
|
|
||||||
|
|
||||||
self.and3 = factory.create(module_type="and3_dec",
|
self.and3 = factory.create(module_type="and3_dec",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and3)
|
|
||||||
# TBD
|
# TBD
|
||||||
# self.and4 = factory.create(module_type="and4_dec")
|
# self.and4 = factory.create(module_type="and4_dec")
|
||||||
# self.add_mod(self.and4)
|
|
||||||
|
|
||||||
self.add_decoders()
|
self.add_decoders()
|
||||||
|
|
||||||
|
|
@ -84,15 +82,12 @@ class hierarchical_decoder(design.design):
|
||||||
""" Create the decoders based on the number of pre-decodes """
|
""" Create the decoders based on the number of pre-decodes """
|
||||||
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
|
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.pre2_4)
|
|
||||||
|
|
||||||
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
|
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.pre3_8)
|
|
||||||
|
|
||||||
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
|
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.pre4_16)
|
|
||||||
|
|
||||||
def determine_predecodes(self, num_inputs):
|
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")
|
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
|
||||||
|
|
||||||
# Two extra pitches between modules on left and right
|
# 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
|
self.row_decoder_height = self.and2.height * self.num_outputs
|
||||||
|
|
||||||
# Extra bus space for supply contacts
|
# Extra bus space for supply contacts
|
||||||
|
|
@ -501,11 +496,11 @@ class hierarchical_decoder(design.design):
|
||||||
if (self.num_inputs >= 4):
|
if (self.num_inputs >= 4):
|
||||||
# This leaves an offset for the predecoder output jogs
|
# 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)]
|
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,
|
self.predecode_bus = self.create_vertical_bus(layer=self.bus_layer,
|
||||||
pitch=self.bus_pitch,
|
pitch=self.bus_pitch,
|
||||||
offset=vector(self.bus_pitch, 0),
|
offset=vector(self.bus_pitch, 0),
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=self.height)
|
length=self.height)
|
||||||
self.route_bus_to_decoder()
|
self.route_bus_to_decoder()
|
||||||
self.route_predecodes_to_bus()
|
self.route_predecodes_to_bus()
|
||||||
|
|
||||||
|
|
@ -593,44 +588,20 @@ class hierarchical_decoder(design.design):
|
||||||
output_index)
|
output_index)
|
||||||
output_index = output_index + 1
|
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:
|
# Leave these to route in the port_address
|
||||||
for n in ["vdd", "gnd"]:
|
all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst
|
||||||
pins = self.and_inst[0].get_pins(n)
|
for inst in all_insts:
|
||||||
for pin in pins:
|
self.copy_layout_pin(inst, "vdd")
|
||||||
self.add_rect(layer=pin.layer,
|
self.copy_layout_pin(inst, "gnd")
|
||||||
offset=pin.ll() + vector(0, self.bus_space),
|
|
||||||
width=pin.width(),
|
|
||||||
height=self.height - 2 * self.bus_space)
|
|
||||||
|
|
||||||
# This adds power vias at the top of each cell
|
self.route_vertical_pins("vdd", self.and_inst, xside="rx",)
|
||||||
# (except the last to keep them inside the boundary)
|
self.route_vertical_pins("gnd", self.and_inst, xside="lx",)
|
||||||
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):
|
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
||||||
"""
|
"""
|
||||||
|
|
@ -698,7 +669,7 @@ class hierarchical_decoder(design.design):
|
||||||
drc_error = 0
|
drc_error = 0
|
||||||
for and_input in self.predecode_bus_rail_pos:
|
for and_input in self.predecode_bus_rail_pos:
|
||||||
if and_input.x == rail_pos.x:
|
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
|
drc_error = 1
|
||||||
if drc_error == 0:
|
if drc_error == 0:
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,9 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
import math
|
import math
|
||||||
from vector import vector
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer_properties as layer_props
|
from tech import layer_properties as layer_props
|
||||||
|
|
@ -18,7 +18,7 @@ from tech import preferred_directions
|
||||||
from tech import drc
|
from tech import drc
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode(design.design):
|
class hierarchical_predecode(design):
|
||||||
"""
|
"""
|
||||||
Pre 2x4 and 3x8 and TBD 4x16 decoder shared code.
|
Pre 2x4 and 3x8 and TBD 4x16 decoder shared code.
|
||||||
"""
|
"""
|
||||||
|
|
@ -59,13 +59,11 @@ class hierarchical_predecode(design.design):
|
||||||
inv_type = "inv_dec"
|
inv_type = "inv_dec"
|
||||||
self.and_mod = factory.create(module_type=and_type,
|
self.and_mod = factory.create(module_type=and_type,
|
||||||
height=self.cell_height)
|
height=self.cell_height)
|
||||||
self.add_mod(self.and_mod)
|
|
||||||
|
|
||||||
# This uses the pinv_dec parameterized cell
|
# This uses the pinv_dec parameterized cell
|
||||||
self.inv = factory.create(module_type=inv_type,
|
self.inv = factory.create(module_type=inv_type,
|
||||||
height=self.cell_height,
|
height=self.cell_height,
|
||||||
size=1)
|
size=1)
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" The general organization is from left to right:
|
""" The general organization is from left to right:
|
||||||
|
|
@ -191,7 +189,7 @@ class hierarchical_predecode(design.design):
|
||||||
self.route_output_inverters()
|
self.route_output_inverters()
|
||||||
self.route_inputs_to_rails()
|
self.route_inputs_to_rails()
|
||||||
self.route_output_ands()
|
self.route_output_ands()
|
||||||
self.route_vdd_gnd()
|
self.route_supplies()
|
||||||
|
|
||||||
def route_inputs_to_rails(self):
|
def route_inputs_to_rails(self):
|
||||||
""" Route the uninverted inputs to the second set of rails """
|
""" Route the uninverted inputs to the second set of rails """
|
||||||
|
|
@ -380,10 +378,10 @@ class hierarchical_predecode(design.design):
|
||||||
offset=pin_pos,
|
offset=pin_pos,
|
||||||
directions=direction)
|
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. """
|
""" 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:
|
if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder:
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
# This makes a wire from top to bottom for both inv and and gates
|
# 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)
|
height=top_pin.uy() - self.bus_pitch)
|
||||||
# This adds power vias at the top of each cell
|
# This adds power vias at the top of each cell
|
||||||
# (except the last to keep them inside the boundary)
|
# (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)
|
pins = i.get_pins(n)
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
self.copy_power_pin(pin, loc=pin.uc())
|
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
|
# In other techs, we are using standard cell decoder cells with horizontal power
|
||||||
else:
|
else:
|
||||||
for num in range(0, self.number_of_outputs):
|
for num in range(0, self.number_of_outputs):
|
||||||
|
|
||||||
# Route both supplies
|
|
||||||
for n in ["vdd", "gnd"]:
|
for n in ["vdd", "gnd"]:
|
||||||
and_pins = self.and_inst[num].get_pins(n)
|
and_pins = self.and_inst[num].get_pins(n)
|
||||||
for and_pin in and_pins:
|
for and_pin in and_pins:
|
||||||
|
|
@ -415,10 +411,9 @@ class hierarchical_predecode(design.design):
|
||||||
end=vector(self.width, and_pin.cy()))
|
end=vector(self.width, and_pin.cy()))
|
||||||
|
|
||||||
# Add pins in two locations
|
# Add pins in two locations
|
||||||
for xoffset in [self.inv_inst[0].lx() - self.bus_space,
|
if n == "vdd":
|
||||||
self.and_inst[0].lx() - self.bus_space]:
|
xoffset = self.and_inst[0].lx() - self.bus_space
|
||||||
pin_pos = vector(xoffset, and_pin.cy())
|
else:
|
||||||
self.copy_power_pin(and_pin, loc=pin_pos)
|
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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from hierarchical_predecode import hierarchical_predecode
|
from .hierarchical_predecode import hierarchical_predecode
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from hierarchical_predecode import hierarchical_predecode
|
from .hierarchical_predecode import hierarchical_predecode
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
from hierarchical_predecode import hierarchical_predecode
|
from .hierarchical_predecode import hierarchical_predecode
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
import logical_effort
|
from base import logical_effort
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from tech import spice, parameter
|
from tech import spice, parameter
|
||||||
|
|
||||||
|
|
||||||
class inv_dec(design.design):
|
class inv_dec(design):
|
||||||
"""
|
"""
|
||||||
INV for address decoders.
|
INV for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
@ -51,12 +51,12 @@ class inv_dec(design.design):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import bitcell_base_array
|
from .bitcell_base_array import bitcell_base_array
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from base import vector
|
||||||
import debug
|
import debug
|
||||||
from tech import layer_properties as layer_props
|
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.
|
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
|
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,
|
rbl=self.rbl,
|
||||||
left_rbl=self.left_rbl,
|
left_rbl=self.left_rbl,
|
||||||
right_rbl=self.right_rbl)
|
right_rbl=self.right_rbl)
|
||||||
self.add_mod(self.bitcell_array)
|
|
||||||
|
|
||||||
self.wl_array = factory.create(module_type="wordline_buffer_array",
|
self.wl_array = factory.create(module_type="wordline_buffer_array",
|
||||||
rows=self.rows + 1,
|
rows=self.rows + 1,
|
||||||
cols=self.cols)
|
cols=self.cols)
|
||||||
self.add_mod(self.wl_array)
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
# Outputs from the wordline driver (by port)
|
# Outputs from the wordline driver (by port)
|
||||||
|
|
@ -161,17 +159,20 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||||
|
|
||||||
def place(self):
|
def place(self):
|
||||||
""" Place the bitcelll array to the right of the wl driver. """
|
""" 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
|
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,
|
bitcell_array_offset = vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0)
|
||||||
0))
|
self.bitcell_array_inst.place(bitcell_array_offset)
|
||||||
|
|
||||||
if len(self.all_ports) > 1:
|
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,
|
wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
|
||||||
2 * self.cell.height + self.wl_array.height),
|
self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height)
|
||||||
|
self.wl_insts[1].place(wl_offset,
|
||||||
mirror="XY")
|
mirror="XY")
|
||||||
|
|
||||||
self.height = self.bitcell_array.height
|
self.height = self.bitcell_array.height
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,14 @@
|
||||||
import sys
|
import sys
|
||||||
from tech import drc, parameter
|
from tech import drc, parameter
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
import math
|
import math
|
||||||
from math import log,sqrt,ceil
|
from math import log,sqrt,ceil
|
||||||
import contact
|
from base import vector
|
||||||
from vector import vector
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
class multibank(design.design):
|
class multibank(design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated a single bank including bitcell array,
|
Dynamically generated a single bank including bitcell array,
|
||||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
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,
|
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
||||||
rows=self.num_rows)
|
rows=self.num_rows)
|
||||||
self.add_mod(self.bitcell_array)
|
|
||||||
|
|
||||||
self.precharge_array = self.mod_precharge_array(columns=self.num_cols)
|
self.precharge_array = self.mod_precharge_array(columns=self.num_cols)
|
||||||
self.add_mod(self.precharge_array)
|
|
||||||
|
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
|
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
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,
|
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
|
||||||
words_per_row=self.words_per_row)
|
words_per_row=self.words_per_row)
|
||||||
self.add_mod(self.sense_amp_array)
|
|
||||||
|
|
||||||
if self.write_size:
|
if self.write_size:
|
||||||
self.write_mask_driver_array = self.mod_write_mask_driver_array(columns=self.num_cols,
|
self.write_mask_driver_array = self.mod_write_mask_driver_array(columns=self.num_cols,
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
write_size=self.write_size)
|
write_size=self.write_size)
|
||||||
self.add_mod(self.write_mask_driver_array)
|
|
||||||
else:
|
else:
|
||||||
self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols,
|
self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
word_size=self.word_size)
|
||||||
self.add_mod(self.write_driver_array)
|
|
||||||
|
|
||||||
self.row_decoder = self.mod_decoder(rows=self.num_rows)
|
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,
|
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
|
||||||
word_size=self.word_size)
|
word_size=self.word_size)
|
||||||
self.add_mod(self.tri_gate_array)
|
|
||||||
|
|
||||||
self.wordline_driver = self.mod_wordline_driver(rows=self.num_rows)
|
self.wordline_driver = self.mod_wordline_driver(rows=self.num_rows)
|
||||||
self.add_mod(self.wordline_driver)
|
|
||||||
|
|
||||||
self.inv = pinv()
|
self.inv = pinv()
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
if(self.num_banks > 1):
|
if(self.num_banks > 1):
|
||||||
self.bank_select = self.mod_bank_select()
|
self.bank_select = self.mod_bank_select()
|
||||||
self.add_mod(self.bank_select)
|
|
||||||
|
|
||||||
|
|
||||||
def add_bitcell_array(self):
|
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)])
|
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
|
# Bring it up to M2 for M2/M3 routing
|
||||||
self.add_via(layers=self.m1_stack,
|
self.add_via(layers=self.m1_stack,
|
||||||
offset=in_pin + contact.m1_via.offset,
|
offset=in_pin + self.m1_via.offset,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
self.add_via(layers=self.m2_stack,
|
self.add_via(layers=self.m2_stack,
|
||||||
offset=in_pin + self.m2m3_via_offset,
|
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)
|
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_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
|
||||||
self.add_via(layers=self.m1_stack,
|
self.add_via(layers=self.m1_stack,
|
||||||
offset=in_pin + contact.m1_via.offset,
|
offset=in_pin + self.m1_via.offset,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
self.add_via(layers=self.m2_stack,
|
self.add_via(layers=self.m2_stack,
|
||||||
offset=in_pin + self.m2m3_via_offset,
|
offset=in_pin + self.m2m3_via_offset,
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
from tech import spice, parameter, drc
|
from tech import spice, parameter, drc
|
||||||
from tech import cell_properties as props
|
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.
|
2-input NAND decoder for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
@ -56,12 +56,12 @@ class nand2_dec(design.design):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 2
|
parasitic_delay = 2
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
from tech import spice, parameter, drc
|
from tech import spice, parameter, drc
|
||||||
from tech import cell_properties as props
|
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.
|
3-input NAND decoder for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
@ -56,12 +56,12 @@ class nand3_dec(design.design):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 2
|
parasitic_delay = 2
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -5,13 +5,13 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import design
|
from base import design
|
||||||
from tech import spice, parameter, drc
|
from tech import spice, parameter, drc
|
||||||
from tech import cell_properties as props
|
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.
|
4-input NAND decoder for address decoders.
|
||||||
"""
|
"""
|
||||||
|
|
@ -56,12 +56,12 @@ class nand4_dec(design.design):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 2
|
parasitic_delay = 2
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# 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 tech import drc, spice
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
@ -47,7 +47,6 @@ class bitcell_array(bitcell_base_array):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the modules used in this design """
|
""" Add the modules used in this design """
|
||||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||||
self.add_mod(self.cell)
|
|
||||||
|
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import vector
|
||||||
import pgate
|
from .pgate import *
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class pand2(pgate.pgate):
|
class pand2(pgate):
|
||||||
"""
|
"""
|
||||||
This is an AND (or NAND) with configurable drive strength.
|
This is an AND (or NAND) with configurable drive strength.
|
||||||
"""
|
"""
|
||||||
|
|
@ -32,16 +32,13 @@ class pand2(pgate.pgate):
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
self.nand = factory.create(module_type="pnand2",
|
self.nand = factory.create(module_type="pnand2",
|
||||||
height=self.height,
|
height=self.height,
|
||||||
add_wells=self.vertical)
|
add_wells=False)
|
||||||
|
|
||||||
self.inv = factory.create(module_type="pdriver",
|
self.inv = factory.create(module_type="pdriver",
|
||||||
size_list=[self.size],
|
size_list=[self.size],
|
||||||
height=self.height,
|
height=self.height,
|
||||||
add_wells=self.add_wells)
|
add_wells=self.add_wells)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
self.height = 2 * self.nand.height
|
self.height = 2 * self.nand.height
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import vector
|
||||||
import pgate
|
from .pgate import *
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class pand3(pgate.pgate):
|
class pand3(pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving loads.
|
This is a simple buffer used for driving loads.
|
||||||
"""
|
"""
|
||||||
|
|
@ -43,9 +43,6 @@ class pand3(pgate.pgate):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
add_wells=self.add_wells)
|
add_wells=self.add_wells)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
self.height = 2 * self.nand.height
|
self.height = 2 * self.nand.height
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import vector
|
||||||
import pgate
|
from .pgate import *
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class pand4(pgate.pgate):
|
class pand4(pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving loads.
|
This is a simple buffer used for driving loads.
|
||||||
"""
|
"""
|
||||||
|
|
@ -43,9 +43,6 @@ class pand4(pgate.pgate):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
add_wells=self.add_wells)
|
add_wells=self.add_wells)
|
||||||
|
|
||||||
self.add_mod(self.nand)
|
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
if self.vertical:
|
if self.vertical:
|
||||||
self.height = 2 * self.nand.height
|
self.height = 2 * self.nand.height
|
||||||
|
|
@ -162,4 +159,3 @@ class pand4(pgate.pgate):
|
||||||
slew=nand_delay.slew,
|
slew=nand_delay.slew,
|
||||||
load=load)
|
load=load)
|
||||||
return nand_delay + inv_delay
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
|
|
@ -5,18 +5,17 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
|
||||||
import debug
|
import debug
|
||||||
|
from base import logical_effort
|
||||||
|
from base import vector
|
||||||
from tech import drc, parameter, layer
|
from tech import drc, parameter, layer
|
||||||
from tech import cell_properties as props
|
from tech import cell_properties as props
|
||||||
from vector import vector
|
|
||||||
from ptx import ptx
|
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import logical_effort
|
from .ptx import ptx
|
||||||
import bitcell_base
|
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,
|
This module implements a parametrically sized multi-port bitcell,
|
||||||
with a variable number of read/write, write, and read ports
|
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.mirror = props.bitcell_1port.mirror
|
||||||
self.end_caps = props.bitcell_1port.end_caps
|
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"
|
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
|
||||||
info_string = fmt_str.format(self.num_rw_ports,
|
info_string = fmt_str.format(self.num_rw_ports,
|
||||||
self.num_w_ports,
|
self.num_w_ports,
|
||||||
|
|
@ -87,7 +98,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
self.route_wordlines()
|
self.route_wordlines()
|
||||||
self.route_bitlines()
|
self.route_bitlines()
|
||||||
self.route_supply()
|
self.route_supplies()
|
||||||
|
|
||||||
if self.replica_bitcell:
|
if self.replica_bitcell:
|
||||||
self.route_rbc_short()
|
self.route_rbc_short()
|
||||||
|
|
@ -179,26 +190,21 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
# create ptx for inverter transistors
|
# create ptx for inverter transistors
|
||||||
self.inverter_nmos = ptx(width=inverter_nmos_width,
|
self.inverter_nmos = ptx(width=inverter_nmos_width,
|
||||||
tx_type="nmos")
|
tx_type="nmos")
|
||||||
self.add_mod(self.inverter_nmos)
|
|
||||||
|
|
||||||
self.inverter_pmos = ptx(width=inverter_pmos_width,
|
self.inverter_pmos = ptx(width=inverter_pmos_width,
|
||||||
tx_type="pmos")
|
tx_type="pmos")
|
||||||
self.add_mod(self.inverter_pmos)
|
|
||||||
|
|
||||||
# create ptx for readwrite transitors
|
# create ptx for readwrite transitors
|
||||||
self.readwrite_nmos = ptx(width=readwrite_nmos_width,
|
self.readwrite_nmos = ptx(width=readwrite_nmos_width,
|
||||||
tx_type="nmos")
|
tx_type="nmos")
|
||||||
self.add_mod(self.readwrite_nmos)
|
|
||||||
|
|
||||||
# create ptx for write transitors
|
# create ptx for write transitors
|
||||||
self.write_nmos = ptx(width=write_nmos_width,
|
self.write_nmos = ptx(width=write_nmos_width,
|
||||||
tx_type="nmos")
|
tx_type="nmos")
|
||||||
self.add_mod(self.write_nmos)
|
|
||||||
|
|
||||||
# create ptx for read transistors
|
# create ptx for read transistors
|
||||||
self.read_nmos = ptx(width=read_nmos_width,
|
self.read_nmos = ptx(width=read_nmos_width,
|
||||||
tx_type="nmos")
|
tx_type="nmos")
|
||||||
self.add_mod(self.read_nmos)
|
|
||||||
|
|
||||||
def calculate_spacing(self):
|
def calculate_spacing(self):
|
||||||
""" Calculate transistor spacings """
|
""" Calculate transistor spacings """
|
||||||
|
|
@ -215,20 +221,20 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
# y-offset for the access transistor's gate contact
|
# y-offset for the access transistor's gate contact
|
||||||
self.gate_contact_yoffset = max_contact_extension + self.m2_space \
|
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
|
# 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
|
# y-position of inverter nmos
|
||||||
self.inverter_nmos_ypos = self.port_ypos
|
self.inverter_nmos_ypos = self.port_ypos
|
||||||
|
|
||||||
# spacing between ports (same for read/write and write ports)
|
# spacing between ports (same for read/write and write ports)
|
||||||
self.bitline_offset = -0.5 * self.readwrite_nmos.active_width \
|
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
|
+ self.m2_space + self.m2_width
|
||||||
m2_constraint = self.bitline_offset + self.m2_space \
|
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
|
- 0.5 * self.readwrite_nmos.active_width
|
||||||
self.write_port_spacing = max(self.active_space,
|
self.write_port_spacing = max(self.active_space,
|
||||||
self.m1_space,
|
self.m1_space,
|
||||||
|
|
@ -236,7 +242,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.read_port_spacing = self.bitline_offset + self.m2_space
|
self.read_port_spacing = self.bitline_offset + self.m2_space
|
||||||
|
|
||||||
# spacing between cross coupled inverters
|
# 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
|
# calculations related to inverter connections
|
||||||
inverter_pmos_contact_extension = 0.5 * \
|
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_nmos.active_contact.height - self.inverter_nmos.active_height)
|
||||||
self.inverter_gap = max(self.poly_to_active,
|
self.inverter_gap = max(self.poly_to_active,
|
||||||
self.m1_space + inverter_nmos_contact_extension) \
|
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.m1_space + inverter_pmos_contact_extension
|
||||||
self.cross_couple_lower_ypos = self.inverter_nmos_ypos \
|
self.cross_couple_lower_ypos = self.inverter_nmos_ypos \
|
||||||
+ self.inverter_nmos.active_height \
|
+ self.inverter_nmos.active_height \
|
||||||
+ max(self.poly_to_active,
|
+ max(self.poly_to_active,
|
||||||
self.m1_space + inverter_nmos_contact_extension) \
|
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.cross_couple_upper_ypos = self.inverter_nmos_ypos \
|
||||||
+ self.inverter_nmos.active_height \
|
+ self.inverter_nmos.active_height \
|
||||||
+ max(self.poly_to_active,
|
+ max(self.poly_to_active,
|
||||||
self.m1_space + inverter_nmos_contact_extension) \
|
self.m1_space + inverter_nmos_contact_extension) \
|
||||||
+ self.poly_to_contact \
|
+ self.poly_to_contact \
|
||||||
+ 1.5 * contact.poly_contact.width
|
+ 1.5 * self.poly_contact.width
|
||||||
|
|
||||||
# spacing between wordlines (and gnd)
|
# spacing between wordlines (and gnd)
|
||||||
self.m1_offset = -0.5 * self.m1_width
|
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.write_nmos.active_width + self.write_port_spacing) \
|
||||||
- self.num_r_ports * \
|
- self.num_r_ports * \
|
||||||
(self.read_port_width + self.read_port_spacing) \
|
(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.width = -2 * self.leftmost_xpos
|
||||||
self.height = self.topmost_ypos - self.botmost_ypos
|
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
|
# add contacts to connect gate poly to drain/source
|
||||||
# metal1 (to connect Q to Q_bar)
|
# metal1 (to connect Q to Q_bar)
|
||||||
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
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.cross_couple_upper_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=contact_offset_left,
|
offset=contact_offset_left,
|
||||||
directions=("H", "H"))
|
directions=("H", "H"))
|
||||||
|
|
||||||
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
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.cross_couple_lower_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=contact_offset_right,
|
offset=contact_offset_right,
|
||||||
|
|
@ -407,24 +413,24 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
if OPTS.use_pex:
|
if OPTS.use_pex:
|
||||||
# add labels to cross couple inverter for extracted simulation
|
# add labels to cross couple inverter for extracted simulation
|
||||||
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
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)
|
self.cross_couple_upper_ypos)
|
||||||
|
|
||||||
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
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.cross_couple_lower_ypos)
|
||||||
self.add_pex_labels(contact_offset_left_output, contact_offset_right_output)
|
self.add_pex_labels(contact_offset_left_output, contact_offset_right_output)
|
||||||
|
|
||||||
def route_rails(self):
|
def route_rails(self):
|
||||||
""" Adds gnd and vdd rails and connects them to the inverters """
|
""" Adds gnd and vdd rails and connects them to the inverters """
|
||||||
|
|
||||||
# Add rails for vdd and gnd
|
# Add rails for vdd and gnd
|
||||||
gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
|
gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
|
||||||
self.gnd_position = vector(0, gnd_ypos)
|
self.gnd_position = vector(0, gnd_ypos)
|
||||||
self.add_rect_center(layer="m1",
|
self.add_layout_pin_rect_center(text="gnd",
|
||||||
offset=self.gnd_position,
|
layer="m1",
|
||||||
width=self.width)
|
offset=self.gnd_position,
|
||||||
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
|
width=self.width)
|
||||||
|
|
||||||
|
|
||||||
vdd_ypos = self.inverter_nmos_ypos \
|
vdd_ypos = self.inverter_nmos_ypos \
|
||||||
+ self.inverter_nmos.active_height \
|
+ self.inverter_nmos.active_height \
|
||||||
|
|
@ -432,10 +438,10 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
+ self.inverter_pmos.active_height \
|
+ self.inverter_pmos.active_height \
|
||||||
+ self.vdd_offset
|
+ self.vdd_offset
|
||||||
self.vdd_position = vector(0, vdd_ypos)
|
self.vdd_position = vector(0, vdd_ypos)
|
||||||
self.add_rect_center(layer="m1",
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
offset=self.vdd_position,
|
layer="m1",
|
||||||
width=self.width)
|
offset=self.vdd_position,
|
||||||
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
|
width=self.width)
|
||||||
|
|
||||||
def create_readwrite_ports(self):
|
def create_readwrite_ports(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -475,6 +481,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
self.connect_inst([self.Q_bar,
|
self.connect_inst([self.Q_bar,
|
||||||
self.rw_wl_names[k], br_name, "gnd"])
|
self.rw_wl_names[k], br_name, "gnd"])
|
||||||
|
|
||||||
|
|
||||||
def place_readwrite_ports(self):
|
def place_readwrite_ports(self):
|
||||||
""" Places read/write ports in the bit cell """
|
""" Places read/write ports in the bit cell """
|
||||||
# define read/write transistor variables as empty arrays
|
# define read/write transistor variables as empty arrays
|
||||||
|
|
@ -529,6 +536,21 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
offset=self.rwbr_positions[k],
|
offset=self.rwbr_positions[k],
|
||||||
height=self.height)
|
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
|
# update furthest left and right transistor edges
|
||||||
self.left_building_edge = left_readwrite_transistor_xpos
|
self.left_building_edge = left_readwrite_transistor_xpos
|
||||||
self.right_building_edge = right_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],
|
offset=self.wbr_positions[k],
|
||||||
height=self.height)
|
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
|
# update furthest left and right transistor edges
|
||||||
self.left_building_edge = left_write_transistor_xpos
|
self.left_building_edge = left_write_transistor_xpos
|
||||||
self.right_building_edge = right_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],
|
offset=self.rbr_positions[k],
|
||||||
height=self.height)
|
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):
|
def route_wordlines(self):
|
||||||
""" Routes gate of transistors to their respective wordlines """
|
""" Routes gate of transistors to their respective wordlines """
|
||||||
port_transistors = []
|
port_transistors = []
|
||||||
|
|
@ -845,7 +895,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
directions="nonpref")
|
directions="nonpref")
|
||||||
|
|
||||||
self.add_path("m2",
|
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):
|
for k in range(self.total_ports):
|
||||||
port_contact_offest = right_port_transistors[k].get_pin("D").center()
|
port_contact_offest = right_port_transistors[k].get_pin("D").center()
|
||||||
|
|
@ -858,9 +908,9 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
directions="nonpref")
|
directions="nonpref")
|
||||||
|
|
||||||
self.add_path("m2",
|
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. Route inverter pmos to vdd. """
|
||||||
# route inverter nmos and read-access nmos to gnd
|
# route inverter nmos and read-access nmos to gnd
|
||||||
nmos_contact_positions = []
|
nmos_contact_positions = []
|
||||||
|
|
@ -877,9 +927,9 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
|
|
||||||
|
|
||||||
if position.x > 0:
|
if position.x > 0:
|
||||||
contact_correct = 0.5 * contact.m1_via.height
|
contact_correct = 0.5 * self.m1_via.height
|
||||||
else:
|
else:
|
||||||
contact_correct = -0.5 * contact.m1_via.height
|
contact_correct = -0.5 * self.m1_via.height
|
||||||
supply_offset = vector(position.x + contact_correct,
|
supply_offset = vector(position.x + contact_correct,
|
||||||
self.gnd_position.y)
|
self.gnd_position.y)
|
||||||
self.add_via_center(layers=self.m1_stack,
|
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
|
# add poly to metal1 contacts for gates of the inverters
|
||||||
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \
|
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.cross_couple_upper_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=left_storage_contact,
|
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 \
|
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.cross_couple_upper_ypos)
|
||||||
self.add_via_center(layers=self.poly_stack,
|
self.add_via_center(layers=self.poly_stack,
|
||||||
offset=right_storage_contact,
|
offset=right_storage_contact,
|
||||||
|
|
@ -1013,7 +1063,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
well_height = max_nmos_well_height + self.port_ypos \
|
well_height = max_nmos_well_height + self.port_ypos \
|
||||||
- self.nwell_enclose_active - self.gnd_position.y
|
- self.nwell_enclose_active - self.gnd_position.y
|
||||||
# FIXME fudge factor xpos
|
# 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)
|
offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos)
|
||||||
self.add_rect(layer="pwell",
|
self.add_rect(layer="pwell",
|
||||||
offset=offset,
|
offset=offset,
|
||||||
|
|
@ -1141,12 +1191,12 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
# min size NMOS gate load
|
# min size NMOS gate load
|
||||||
read_port_load = self.num_r_ports / 2
|
read_port_load = self.num_r_ports / 2
|
||||||
total_load = load + read_port_load + write_port_load
|
total_load = load + read_port_load + write_port_load
|
||||||
return logical_effort.logical_effort('bitline',
|
return logical_effort('bitline',
|
||||||
size,
|
size,
|
||||||
cin,
|
cin,
|
||||||
load + read_port_load,
|
load + read_port_load,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
False)
|
False)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
""" Return the relative capacitance of the access transistor gates """
|
""" Return the relative capacitance of the access transistor gates """
|
||||||
|
|
@ -1172,4 +1222,3 @@ class pbitcell(bitcell_base.bitcell_base):
|
||||||
for wl, bl, br in pin_zip:
|
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[bl], self)
|
||||||
graph.add_edge(pin_dict[wl], pin_dict[br], self)
|
graph.add_edge(pin_dict[wl], pin_dict[br], self)
|
||||||
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import vector
|
||||||
import pgate
|
from .pgate import *
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class pbuf(pgate.pgate):
|
class pbuf(pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving loads.
|
This is a simple buffer used for driving loads.
|
||||||
"""
|
"""
|
||||||
|
|
@ -52,13 +52,11 @@ class pbuf(pgate.pgate):
|
||||||
self.inv1 = factory.create(module_type="pinv",
|
self.inv1 = factory.create(module_type="pinv",
|
||||||
size=input_size,
|
size=input_size,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
self.add_mod(self.inv1)
|
|
||||||
|
|
||||||
self.inv2 = factory.create(module_type="pinv",
|
self.inv2 = factory.create(module_type="pinv",
|
||||||
size=self.size,
|
size=self.size,
|
||||||
height=self.height,
|
height=self.height,
|
||||||
add_wells=False)
|
add_wells=False)
|
||||||
self.add_mod(self.inv2)
|
|
||||||
|
|
||||||
def create_insts(self):
|
def create_insts(self):
|
||||||
self.inv1_inst = self.add_inst(name="buf_inv1",
|
self.inv1_inst = self.add_inst(name="buf_inv1",
|
||||||
|
|
@ -96,4 +94,3 @@ class pbuf(pgate.pgate):
|
||||||
offset=a_pin.center(),
|
offset=a_pin.center(),
|
||||||
width=a_pin.width(),
|
width=a_pin.width(),
|
||||||
height=a_pin.height())
|
height=a_pin.height())
|
||||||
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
from vector import vector
|
from base import vector
|
||||||
import pgate
|
from .pgate import *
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class pbuf_dec(pgate.pgate):
|
class pbuf_dec(pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving wordlines.
|
This is a simple buffer used for driving wordlines.
|
||||||
"""
|
"""
|
||||||
|
|
@ -25,7 +25,7 @@ class pbuf_dec(pgate.pgate):
|
||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
# Creates the netlist and layout
|
# Creates the netlist and layout
|
||||||
pgate.pgate.__init__(self, name, height)
|
pgate.__init__(self, name, height)
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
|
@ -52,12 +52,10 @@ class pbuf_dec(pgate.pgate):
|
||||||
self.inv1 = factory.create(module_type="pinv_dec",
|
self.inv1 = factory.create(module_type="pinv_dec",
|
||||||
size=input_size,
|
size=input_size,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
self.add_mod(self.inv1)
|
|
||||||
|
|
||||||
self.inv2 = factory.create(module_type="pinv_dec",
|
self.inv2 = factory.create(module_type="pinv_dec",
|
||||||
size=self.size,
|
size=self.size,
|
||||||
height=self.height)
|
height=self.height)
|
||||||
self.add_mod(self.inv2)
|
|
||||||
|
|
||||||
def create_insts(self):
|
def create_insts(self):
|
||||||
self.inv1_inst = self.add_inst(name="buf_inv1",
|
self.inv1_inst = self.add_inst(name="buf_inv1",
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import pgate
|
from .pgate import *
|
||||||
from vector import vector
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
|
|
||||||
|
|
||||||
class pdriver(pgate.pgate):
|
class pdriver(pgate):
|
||||||
"""
|
"""
|
||||||
This instantiates an even or odd number of inverters
|
This instantiates an even or odd number of inverters
|
||||||
sized for driving a load.
|
sized for driving a load.
|
||||||
|
|
@ -93,7 +93,6 @@ class pdriver(pgate.pgate):
|
||||||
height=self.height,
|
height=self.height,
|
||||||
add_wells=self.add_wells)
|
add_wells=self.add_wells)
|
||||||
self.inv_list.append(temp_inv)
|
self.inv_list.append(temp_inv)
|
||||||
self.add_mod(temp_inv)
|
|
||||||
|
|
||||||
def create_insts(self):
|
def create_insts(self):
|
||||||
self.inv_inst_list = []
|
self.inv_inst_list = []
|
||||||
|
|
@ -5,20 +5,20 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
from base import design
|
||||||
import design
|
from base import vector
|
||||||
import debug
|
import debug
|
||||||
import math
|
import math
|
||||||
from bisect import bisect_left
|
from bisect import bisect_left
|
||||||
from tech import layer, drc
|
from tech import layer, drc
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import cell_properties as cell_props
|
from tech import cell_properties as cell_props
|
||||||
|
|
||||||
if cell_props.ptx.bin_spice_models:
|
if cell_props.ptx.bin_spice_models:
|
||||||
from tech import nmos_bins, pmos_bins
|
from tech import nmos_bins, pmos_bins
|
||||||
|
|
||||||
|
|
||||||
class pgate(design.design):
|
class pgate(design):
|
||||||
"""
|
"""
|
||||||
This is a module that implements some shared
|
This is a module that implements some shared
|
||||||
functions for parameterized gates.
|
functions for parameterized gates.
|
||||||
|
|
@ -113,14 +113,14 @@ class pgate(design.design):
|
||||||
left_gate_offset = vector(nmos_gate_pin.lx(), ypos)
|
left_gate_offset = vector(nmos_gate_pin.lx(), ypos)
|
||||||
|
|
||||||
# Center is completely symmetric.
|
# Center is completely symmetric.
|
||||||
contact_width = contact.poly_contact.width
|
contact_width = self.poly_contact.width
|
||||||
|
|
||||||
if position == "center":
|
if position == "center":
|
||||||
contact_offset = left_gate_offset \
|
contact_offset = left_gate_offset \
|
||||||
+ vector(0.5 * self.poly_width, 0)
|
+ vector(0.5 * self.poly_width, 0)
|
||||||
elif position == "farleft":
|
elif position == "farleft":
|
||||||
contact_offset = left_gate_offset \
|
contact_offset = left_gate_offset \
|
||||||
- vector(0.5 * contact.poly_contact.width, 0)
|
- vector(0.5 * self.poly_contact.width, 0)
|
||||||
elif position == "left":
|
elif position == "left":
|
||||||
contact_offset = left_gate_offset \
|
contact_offset = left_gate_offset \
|
||||||
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
|
- 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)
|
+ left_gate_offset.scale(0.5, 0)
|
||||||
self.add_rect_center(layer="poly",
|
self.add_rect_center(layer="poly",
|
||||||
offset=mid_point,
|
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)
|
width=left_gate_offset.x - contact_offset.x)
|
||||||
|
|
||||||
return via
|
return via
|
||||||
|
|
@ -208,12 +208,15 @@ class pgate(design.design):
|
||||||
# from the top of the well
|
# from the top of the well
|
||||||
# OR align the active with the top of PMOS active.
|
# OR align the active with the top of PMOS active.
|
||||||
max_y_offset = self.height + 0.5 * self.m1_width
|
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,
|
contact_yoffset = min(self.height - 0.5 * self.implant_width,
|
||||||
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.nwell_enclose_active)
|
self.get_tx_insts("pmos")[0].uy()) \
|
||||||
|
- pmos.active_contact.first_layer_height \
|
||||||
|
- self.implant_enclose_active
|
||||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||||
# Offset by half a contact in x and y
|
# Offset by half a contact in x and y
|
||||||
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
|
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
|
||||||
0.5 * pmos.active_contact.first_layer_height)
|
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,
|
self.nwell_contact = self.add_via_center(layers=layer_stack,
|
||||||
offset=contact_offset,
|
offset=contact_offset,
|
||||||
implant_type="n",
|
implant_type="n",
|
||||||
|
|
@ -276,24 +279,34 @@ class pgate(design.design):
|
||||||
rightx=rightx,
|
rightx=rightx,
|
||||||
topy=self.height)
|
topy=self.height)
|
||||||
|
|
||||||
try:
|
self.add_rect(layer="pimplant",
|
||||||
ntap_insts = [self.nwell_contact]
|
offset=vector(0, self.height - 0.5 * self.implant_width),
|
||||||
self.add_enclosure(ntap_insts,
|
width=self.width,
|
||||||
layer="nimplant",
|
height=self.implant_width)
|
||||||
extend=self.implant_enclose_active,
|
self.add_rect(layer="nimplant",
|
||||||
rightx=self.width,
|
offset=vector(0, -0.5 * self.implant_width),
|
||||||
topy=self.height)
|
width=self.width,
|
||||||
except AttributeError:
|
height=self.implant_width)
|
||||||
pass
|
|
||||||
try:
|
|
||||||
ptap_insts = [self.pwell_contact]
|
# try:
|
||||||
self.add_enclosure(ptap_insts,
|
# ntap_insts = [self.nwell_contact]
|
||||||
layer="pimplant",
|
# self.add_enclosure(ntap_insts,
|
||||||
extend=self.implant_enclose_active,
|
# layer="nimplant",
|
||||||
rightx=self.width,
|
# extend=self.implant_enclose_active,
|
||||||
boty=0)
|
# rightx=self.width,
|
||||||
except AttributeError:
|
# topy=self.height)
|
||||||
pass
|
# 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):
|
def add_pwell_contact(self, nmos, nmos_pos):
|
||||||
""" Add an pwell contact next to the given nmos device. """
|
""" 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
|
# To the right a spacing away from the nmos right active edge
|
||||||
contact_xoffset = nmos_pos.x + nmos.active_width \
|
contact_xoffset = nmos_pos.x + nmos.active_width \
|
||||||
+ self.active_space
|
+ self.active_space
|
||||||
# Must be at least an well enclosure of active up
|
# Allow an nimplant below it under the rail
|
||||||
# from the bottom of the well
|
contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active,
|
||||||
contact_yoffset = max(nmos_pos.y,
|
self.get_tx_insts("nmos")[0].by())
|
||||||
self.nwell_enclose_active \
|
|
||||||
- nmos.active_contact.first_layer_height / 2)
|
|
||||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||||
|
|
||||||
# Offset by half a contact
|
# Offset by half a contact
|
||||||
|
|
@ -362,7 +373,7 @@ class pgate(design.design):
|
||||||
# It was already set or is left as default (minimum)
|
# 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
|
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
||||||
if self.add_wells:
|
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.
|
# Height is an input parameter, so it is not recomputed.
|
||||||
else:
|
else:
|
||||||
max_active_xoffset = self.find_highest_layer_coords("active").x
|
max_active_xoffset = self.find_highest_layer_coords("active").x
|
||||||
|
|
@ -5,22 +5,21 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
|
||||||
import pgate
|
|
||||||
import debug
|
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
|
import operator
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from vector import vector
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from utils import round_to_grid
|
|
||||||
import logical_effort
|
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from errors import drc_error
|
|
||||||
from tech import cell_properties as cell_props
|
from tech import cell_properties as cell_props
|
||||||
|
|
||||||
|
|
||||||
class pinv(pgate.pgate):
|
class pinv(pgate):
|
||||||
"""
|
"""
|
||||||
Pinv generates gds of a parametrically sized inverter. The
|
Pinv generates gds of a parametrically sized inverter. The
|
||||||
size is specified as the drive size (relative to minimum NMOS) and
|
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.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||||
if cell_props.ptx.bin_spice_models:
|
if cell_props.ptx.bin_spice_models:
|
||||||
(self.nmos_width, self.tx_mults) = pgate.pgate.best_bin("nmos", self.nmos_width)
|
(self.nmos_width, self.tx_mults) = pgate.best_bin("nmos", self.nmos_width)
|
||||||
(self.pmos_width, self.tx_mults) = pgate.pgate.best_bin("pmos", self.pmos_width)
|
(self.pmos_width, self.tx_mults) = pgate.best_bin("pmos", self.pmos_width)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Do a quick sanity check and bail if unlikely feasible height
|
# Do a quick sanity check and bail if unlikely feasible height
|
||||||
|
|
@ -106,8 +105,8 @@ class pinv(pgate.pgate):
|
||||||
tx_type="pmos")
|
tx_type="pmos")
|
||||||
tx_height = nmos.poly_height + pmos.poly_height
|
tx_height = nmos.poly_height + pmos.poly_height
|
||||||
# rotated m1 pitch or poly to active spacing
|
# rotated m1 pitch or poly to active spacing
|
||||||
min_channel = max(contact.poly_contact.width + self.m1_space,
|
min_channel = max(self.poly_contact.width + self.m1_space,
|
||||||
contact.poly_contact.width + 2 * self.poly_to_active)
|
self.poly_contact.width + 2 * self.poly_to_active)
|
||||||
|
|
||||||
total_height = tx_height + min_channel + 2 * self.top_bottom_space
|
total_height = tx_height + min_channel + 2 * self.top_bottom_space
|
||||||
# debug.check(self.height > total_height,
|
# debug.check(self.height > total_height,
|
||||||
|
|
@ -207,7 +206,6 @@ class pinv(pgate.pgate):
|
||||||
add_drain_contact=self.route_layer,
|
add_drain_contact=self.route_layer,
|
||||||
connect_poly=True,
|
connect_poly=True,
|
||||||
connect_drain_active=True)
|
connect_drain_active=True)
|
||||||
self.add_mod(self.nmos)
|
|
||||||
|
|
||||||
self.pmos = factory.create(module_type="ptx",
|
self.pmos = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -217,7 +215,6 @@ class pinv(pgate.pgate):
|
||||||
add_drain_contact=self.route_layer,
|
add_drain_contact=self.route_layer,
|
||||||
connect_poly=True,
|
connect_poly=True,
|
||||||
connect_drain_active=True)
|
connect_drain_active=True)
|
||||||
self.add_mod(self.pmos)
|
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -324,12 +321,12 @@ class pinv(pgate.pgate):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 1
|
parasitic_delay = 1
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -340,7 +337,6 @@ class pinv(pgate.pgate):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_on_resistance(self):
|
def get_on_resistance(self):
|
||||||
|
|
@ -5,17 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
|
||||||
import pinv
|
|
||||||
import debug
|
import debug
|
||||||
|
from base import vector
|
||||||
|
from .pinv import pinv
|
||||||
from tech import drc, parameter, layer
|
from tech import drc, parameter, layer
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from tech import cell_properties as cell_props
|
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.
|
This is another version of pinv but with layout for the decoder.
|
||||||
Other stuff is the same (netlist, sizes, etc.)
|
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])
|
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
|
||||||
|
|
||||||
# Center is completely symmetric.
|
# Center is completely symmetric.
|
||||||
contact_width = contact.poly_contact.width
|
contact_width = self.poly_contact.width
|
||||||
contact_offset = nmos_gate_pin.lc() \
|
contact_offset = nmos_gate_pin.lc() \
|
||||||
- vector(self.poly_extend_active + 0.5 * contact_width, 0)
|
- vector(self.poly_extend_active + 0.5 * contact_width, 0)
|
||||||
via = self.add_via_stack_center(from_layer="poly",
|
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
|
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
|
# 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_pos = vector(x_offset, y_offset)
|
||||||
self.nmos_inst.place(self.nmos_pos,
|
self.nmos_inst.place(self.nmos_pos,
|
||||||
rotate=270)
|
rotate=270)
|
||||||
|
|
@ -192,20 +191,20 @@ class pinv_dec(pinv.pinv):
|
||||||
|
|
||||||
source_pos = self.pmos_inst.get_pin("S").center()
|
source_pos = self.pmos_inst.get_pin("S").center()
|
||||||
contact_pos = vector(source_pos.x, self.height)
|
contact_pos = vector(source_pos.x, self.height)
|
||||||
self.nwell_contact = self.add_via_center(layers=self.active_stack,
|
self.add_via_center(layers=self.active_stack,
|
||||||
offset=contact_pos,
|
offset=contact_pos,
|
||||||
implant_type="n",
|
implant_type="n",
|
||||||
well_type="n")
|
well_type="n")
|
||||||
self.add_via_stack_center(offset=contact_pos,
|
self.add_via_stack_center(offset=contact_pos,
|
||||||
from_layer=self.active_stack[2],
|
from_layer=self.active_stack[2],
|
||||||
to_layer=self.supply_layer)
|
to_layer=self.supply_layer)
|
||||||
|
|
||||||
source_pos = self.nmos_inst.get_pin("S").center()
|
source_pos = self.nmos_inst.get_pin("S").center()
|
||||||
contact_pos = vector(source_pos.x, self.height)
|
contact_pos = vector(source_pos.x, self.height)
|
||||||
self.pwell_contact= self.add_via_center(layers=self.active_stack,
|
self.add_via_center(layers=self.active_stack,
|
||||||
offset=contact_pos,
|
offset=contact_pos,
|
||||||
implant_type="p",
|
implant_type="p",
|
||||||
well_type="p")
|
well_type="p")
|
||||||
self.add_via_stack_center(offset=contact_pos,
|
self.add_via_stack_center(offset=contact_pos,
|
||||||
from_layer=self.active_stack[2],
|
from_layer=self.active_stack[2],
|
||||||
to_layer=self.supply_layer)
|
to_layer=self.supply_layer)
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import debug
|
import debug
|
||||||
import pgate
|
from .pgate import *
|
||||||
from vector import vector
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from tech import layer
|
from tech import layer
|
||||||
|
|
||||||
class pinvbuf(pgate.pgate):
|
class pinvbuf(pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple inverter/buffer used for driving loads. It is
|
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.
|
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",
|
self.inv = factory.create(module_type="pinv",
|
||||||
size=input_size,
|
size=input_size,
|
||||||
height=self.row_height)
|
height=self.row_height)
|
||||||
self.add_mod(self.inv)
|
|
||||||
|
|
||||||
self.inv1 = factory.create(module_type="pinv",
|
self.inv1 = factory.create(module_type="pinv",
|
||||||
size=self.predriver_size,
|
size=self.predriver_size,
|
||||||
height=self.row_height)
|
height=self.row_height)
|
||||||
self.add_mod(self.inv1)
|
|
||||||
|
|
||||||
self.inv2 = factory.create(module_type="pinv",
|
self.inv2 = factory.create(module_type="pinv",
|
||||||
size=self.size,
|
size=self.size,
|
||||||
height=self.row_height)
|
height=self.row_height)
|
||||||
self.add_mod(self.inv2)
|
|
||||||
|
|
||||||
def create_insts(self):
|
def create_insts(self):
|
||||||
# Create INV1 (capacitance shield)
|
# Create INV1 (capacitance shield)
|
||||||
|
|
@ -5,17 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import pgate
|
from .pgate import *
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from vector import vector
|
from base import vector
|
||||||
import logical_effort
|
from base import logical_effort
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import contact
|
|
||||||
from tech import cell_properties as cell_props
|
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 module generates gds of a parametrically sized 2-input nand.
|
||||||
This model use ptx to generate a 2-input nand within a cetrain height.
|
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",
|
tx_type="nmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact="active")
|
add_drain_contact="active")
|
||||||
self.add_mod(self.nmos_left)
|
|
||||||
|
|
||||||
self.nmos_right = factory.create(module_type="ptx",
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -87,7 +85,6 @@ class pnand2(pgate.pgate):
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact="active",
|
add_source_contact="active",
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.nmos_right)
|
|
||||||
|
|
||||||
self.pmos_left = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -95,7 +92,6 @@ class pnand2(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_left)
|
|
||||||
|
|
||||||
self.pmos_right = factory.create(module_type="ptx",
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -103,7 +99,6 @@ class pnand2(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_right)
|
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
@ -179,11 +174,11 @@ class pnand2(pgate.pgate):
|
||||||
# Top of NMOS drain
|
# Top of NMOS drain
|
||||||
bottom_pin = self.nmos1_inst.get_pin("D")
|
bottom_pin = self.nmos1_inst.get_pin("D")
|
||||||
# active contact metal to poly contact metal spacing
|
# 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
|
# active diffusion to poly contact spacing
|
||||||
# doesn't use nmos uy because that is calculated using offset + poly height
|
# 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_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
|
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,
|
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||||
active_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
|
self.inputB_yoffset = self.inputA_yoffset + 2 * self.m3_pitch
|
||||||
# # active contact metal to poly contact metal spacing
|
# # 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_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
|
# 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,
|
# self.inputB_yoffset = min(active_contact_to_poly_contact,
|
||||||
# active_to_poly_contact,
|
# active_to_poly_contact,
|
||||||
|
|
@ -219,7 +214,7 @@ class pnand2(pgate.pgate):
|
||||||
""" Route the Z output """
|
""" Route the Z output """
|
||||||
|
|
||||||
# One routing track layer below the PMOS contacts
|
# 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
|
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.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 2
|
parasitic_delay = 2
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -317,7 +312,6 @@ class pnand2(pgate.pgate):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_on_resistance(self):
|
def get_on_resistance(self):
|
||||||
|
|
@ -341,4 +335,3 @@ class pnand2(pgate.pgate):
|
||||||
1,
|
1,
|
||||||
self.tx_mults)
|
self.tx_mults)
|
||||||
return nmos_drain_c + pmos_drain_c
|
return nmos_drain_c + pmos_drain_c
|
||||||
|
|
||||||
|
|
@ -5,17 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import pgate
|
from .pgate import *
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from vector import vector
|
from base import vector
|
||||||
import logical_effort
|
from base import logical_effort
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import contact
|
|
||||||
from tech import cell_properties as cell_props
|
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 module generates gds of a parametrically sized 2-input nand.
|
||||||
This model use ptx to generate a 2-input nand within a cetrain height.
|
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",
|
tx_type="nmos",
|
||||||
add_source_contact="active",
|
add_source_contact="active",
|
||||||
add_drain_contact="active")
|
add_drain_contact="active")
|
||||||
self.add_mod(self.nmos_center)
|
|
||||||
|
|
||||||
self.nmos_right = factory.create(module_type="ptx",
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -90,7 +88,6 @@ class pnand3(pgate.pgate):
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact="active",
|
add_source_contact="active",
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.nmos_right)
|
|
||||||
|
|
||||||
self.nmos_left = factory.create(module_type="ptx",
|
self.nmos_left = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -98,7 +95,6 @@ class pnand3(pgate.pgate):
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact="active")
|
add_drain_contact="active")
|
||||||
self.add_mod(self.nmos_left)
|
|
||||||
|
|
||||||
self.pmos_left = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -106,7 +102,6 @@ class pnand3(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_left)
|
|
||||||
|
|
||||||
self.pmos_center = factory.create(module_type="ptx",
|
self.pmos_center = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -114,7 +109,6 @@ class pnand3(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_center)
|
|
||||||
|
|
||||||
self.pmos_right = factory.create(module_type="ptx",
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -122,7 +116,6 @@ class pnand3(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_right)
|
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
@ -210,17 +203,17 @@ class pnand3(pgate.pgate):
|
||||||
""" Route the A and B and C inputs """
|
""" Route the A and B and C inputs """
|
||||||
|
|
||||||
# We can use this pitch because the contacts and overlap won't be adjacent
|
# 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()
|
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
|
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
|
||||||
|
|
||||||
bottom_pin = self.nmos1_inst.get_pin("D")
|
bottom_pin = self.nmos1_inst.get_pin("D")
|
||||||
# active contact metal to poly contact metal spacing
|
# 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
|
# active diffusion to poly contact spacing
|
||||||
# doesn't use nmos uy because that is calculated using offset + poly height
|
# 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_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
|
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,
|
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||||
active_to_poly_contact,
|
active_to_poly_contact,
|
||||||
|
|
@ -334,12 +327,12 @@ class pnand3(pgate.pgate):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 3
|
parasitic_delay = 3
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -350,7 +343,6 @@ class pnand3(pgate.pgate):
|
||||||
|
|
||||||
def is_non_inverting(self):
|
def is_non_inverting(self):
|
||||||
"""Return input to output polarity for module"""
|
"""Return input to output polarity for module"""
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_on_resistance(self):
|
def get_on_resistance(self):
|
||||||
|
|
@ -5,17 +5,16 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import pgate
|
from .pgate import *
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from vector import vector
|
from base import vector
|
||||||
import logical_effort
|
from base import logical_effort
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
import contact
|
|
||||||
from tech import cell_properties as cell_props
|
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 module generates gds of a parametrically sized 4-input nand.
|
||||||
This model use ptx to generate a 4-input nand within a cetrain height.
|
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",
|
tx_type="nmos",
|
||||||
add_source_contact="active",
|
add_source_contact="active",
|
||||||
add_drain_contact="active")
|
add_drain_contact="active")
|
||||||
self.add_mod(self.nmos_center)
|
|
||||||
|
|
||||||
self.nmos_right = factory.create(module_type="ptx",
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -90,7 +88,6 @@ class pnand4(pgate.pgate):
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact="active",
|
add_source_contact="active",
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.nmos_right)
|
|
||||||
|
|
||||||
self.nmos_left = factory.create(module_type="ptx",
|
self.nmos_left = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -98,7 +95,6 @@ class pnand4(pgate.pgate):
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact="active")
|
add_drain_contact="active")
|
||||||
self.add_mod(self.nmos_left)
|
|
||||||
|
|
||||||
self.pmos_left = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -106,7 +102,6 @@ class pnand4(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_left)
|
|
||||||
|
|
||||||
self.pmos_center = factory.create(module_type="ptx",
|
self.pmos_center = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -114,7 +109,6 @@ class pnand4(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_center)
|
|
||||||
|
|
||||||
self.pmos_right = factory.create(module_type="ptx",
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -122,7 +116,6 @@ class pnand4(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_right)
|
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
@ -231,11 +224,11 @@ class pnand4(pgate.pgate):
|
||||||
|
|
||||||
bottom_pin = self.nmos1_inst.get_pin("D")
|
bottom_pin = self.nmos1_inst.get_pin("D")
|
||||||
# active contact metal to poly contact metal spacing
|
# 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
|
# active diffusion to poly contact spacing
|
||||||
# doesn't use nmos uy because that is calculated using offset + poly height
|
# 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_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
|
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,
|
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||||
active_to_poly_contact,
|
active_to_poly_contact,
|
||||||
|
|
@ -356,12 +349,12 @@ class pnand4(pgate.pgate):
|
||||||
Input inverted by this stage.
|
Input inverted by this stage.
|
||||||
"""
|
"""
|
||||||
parasitic_delay = 3
|
parasitic_delay = 3
|
||||||
return logical_effort.logical_effort(self.name,
|
return logical_effort(self.name,
|
||||||
self.size,
|
self.size,
|
||||||
self.input_load(),
|
self.input_load(),
|
||||||
cout,
|
cout,
|
||||||
parasitic_delay,
|
parasitic_delay,
|
||||||
not inp_is_rise)
|
not inp_is_rise)
|
||||||
|
|
||||||
def build_graph(self, graph, inst_name, port_nets):
|
def build_graph(self, graph, inst_name, port_nets):
|
||||||
"""
|
"""
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import pgate
|
from .pgate import *
|
||||||
import debug
|
import debug
|
||||||
from tech import drc, parameter, spice
|
from tech import drc, parameter, spice
|
||||||
from vector import vector
|
from base import vector
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from tech import cell_properties as cell_props
|
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 module generates gds of a parametrically sized 2-input nor.
|
||||||
This model use ptx to generate a 2-input nor within a cetrain height.
|
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",
|
tx_type="nmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.nmos_left)
|
|
||||||
|
|
||||||
self.nmos_right = factory.create(module_type="ptx",
|
self.nmos_right = factory.create(module_type="ptx",
|
||||||
width=self.nmos_width,
|
width=self.nmos_width,
|
||||||
|
|
@ -85,7 +84,6 @@ class pnor2(pgate.pgate):
|
||||||
tx_type="nmos",
|
tx_type="nmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.nmos_right)
|
|
||||||
|
|
||||||
self.pmos_left = factory.create(module_type="ptx",
|
self.pmos_left = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -93,7 +91,6 @@ class pnor2(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact=self.route_layer,
|
add_source_contact=self.route_layer,
|
||||||
add_drain_contact="active")
|
add_drain_contact="active")
|
||||||
self.add_mod(self.pmos_left)
|
|
||||||
|
|
||||||
self.pmos_right = factory.create(module_type="ptx",
|
self.pmos_right = factory.create(module_type="ptx",
|
||||||
width=self.pmos_width,
|
width=self.pmos_width,
|
||||||
|
|
@ -101,7 +98,6 @@ class pnor2(pgate.pgate):
|
||||||
tx_type="pmos",
|
tx_type="pmos",
|
||||||
add_source_contact="active",
|
add_source_contact="active",
|
||||||
add_drain_contact=self.route_layer)
|
add_drain_contact=self.route_layer)
|
||||||
self.add_mod(self.pmos_right)
|
|
||||||
|
|
||||||
def setup_layout_constants(self):
|
def setup_layout_constants(self):
|
||||||
""" Pre-compute some handy layout parameters. """
|
""" Pre-compute some handy layout parameters. """
|
||||||
|
|
@ -5,15 +5,15 @@
|
||||||
#
|
#
|
||||||
from math import log, ceil
|
from math import log, ceil
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from vector import vector
|
from base import vector
|
||||||
from tech import layer, drc
|
from tech import layer, drc
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import layer_properties as layer_props
|
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)..
|
Create the address port (row decoder and wordline driver)..
|
||||||
"""
|
"""
|
||||||
|
|
@ -76,15 +76,18 @@ class port_address(design.design):
|
||||||
|
|
||||||
def route_supplies(self):
|
def route_supplies(self):
|
||||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||||
for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]:
|
if layer_props.wordline_driver.vertical_supply:
|
||||||
self.copy_power_pins(inst, "vdd")
|
self.copy_layout_pin(self.rbl_driver_inst, "vdd")
|
||||||
self.copy_power_pins(inst, "gnd")
|
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"):
|
self.copy_layout_pin(self.wordline_driver_array_inst, "vdd")
|
||||||
if layer_props.port_address.supply_offset:
|
self.copy_layout_pin(self.wordline_driver_array_inst, "gnd")
|
||||||
self.copy_power_pin(rbl_vdd_pin)
|
|
||||||
else:
|
self.copy_layout_pin(self.row_decoder_inst, "vdd")
|
||||||
self.copy_power_pin(rbl_vdd_pin, loc=rbl_vdd_pin.lc())
|
self.copy_layout_pin(self.row_decoder_inst, "gnd")
|
||||||
|
|
||||||
# Also connect the B input of the RBL and_dec to vdd
|
# Also connect the B input of the RBL and_dec to vdd
|
||||||
if OPTS.local_array_size == 0:
|
if OPTS.local_array_size == 0:
|
||||||
|
|
@ -145,12 +148,10 @@ class port_address(design.design):
|
||||||
|
|
||||||
self.row_decoder = factory.create(module_type="decoder",
|
self.row_decoder = factory.create(module_type="decoder",
|
||||||
num_outputs=self.num_rows)
|
num_outputs=self.num_rows)
|
||||||
self.add_mod(self.row_decoder)
|
|
||||||
|
|
||||||
self.wordline_driver_array = factory.create(module_type="wordline_driver_array",
|
self.wordline_driver_array = factory.create(module_type="wordline_driver_array",
|
||||||
rows=self.num_rows,
|
rows=self.num_rows,
|
||||||
cols=self.num_cols)
|
cols=self.num_cols)
|
||||||
self.add_mod(self.wordline_driver_array)
|
|
||||||
|
|
||||||
local_array_size = OPTS.local_array_size
|
local_array_size = OPTS.local_array_size
|
||||||
if local_array_size > 0:
|
if local_array_size > 0:
|
||||||
|
|
@ -174,8 +175,6 @@ class port_address(design.design):
|
||||||
size=driver_size,
|
size=driver_size,
|
||||||
height=b.height)
|
height=b.height)
|
||||||
|
|
||||||
self.add_mod(self.rbl_driver)
|
|
||||||
|
|
||||||
def create_row_decoder(self):
|
def create_row_decoder(self):
|
||||||
""" Create the hierarchical row decoder """
|
""" 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)
|
wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0)
|
||||||
self.wordline_driver_array_inst.place(wordline_driver_array_offset)
|
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
|
# This m4_pitch corresponds to the offset space for jog routing in the
|
||||||
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
# wordline_driver_array
|
||||||
x_offset = self.wordline_driver_array_inst.rx() - well_gap - self.rbl_driver.width
|
rbl_driver_offset = wordline_driver_array_offset + vector(2 * self.m4_pitch, 0)
|
||||||
|
|
||||||
if self.port == 0:
|
if self.port == 0:
|
||||||
rbl_driver_offset = vector(x_offset,
|
|
||||||
0)
|
|
||||||
self.rbl_driver_inst.place(rbl_driver_offset, "MX")
|
self.rbl_driver_inst.place(rbl_driver_offset, "MX")
|
||||||
else:
|
else:
|
||||||
rbl_driver_offset = vector(x_offset,
|
rbl_driver_offset += vector(0,
|
||||||
self.wordline_driver_array.height)
|
self.wordline_driver_array.height)
|
||||||
self.rbl_driver_inst.place(rbl_driver_offset)
|
self.rbl_driver_inst.place(rbl_driver_offset)
|
||||||
|
|
||||||
# Pass this up
|
# Pass this up
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,17 @@
|
||||||
#
|
#
|
||||||
from tech import drc
|
from tech import drc
|
||||||
import debug
|
import debug
|
||||||
import design
|
from base import design
|
||||||
import math
|
import math
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from tech import cell_properties
|
from tech import cell_properties
|
||||||
from tech import layer_properties as layer_props
|
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.
|
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.
|
Port 0 always has the RBL on the left while port 1 is on the right.
|
||||||
|
|
@ -215,7 +215,6 @@ class port_data(design.design):
|
||||||
bitcell_bl=self.bl_names[self.port],
|
bitcell_bl=self.bl_names[self.port],
|
||||||
bitcell_br=self.br_names[self.port],
|
bitcell_br=self.br_names[self.port],
|
||||||
column_offset=self.port - 1)
|
column_offset=self.port - 1)
|
||||||
self.add_mod(self.precharge_array)
|
|
||||||
|
|
||||||
if self.port in self.read_ports:
|
if self.port in self.read_ports:
|
||||||
# RBLs don't get a sense amp
|
# RBLs don't get a sense amp
|
||||||
|
|
@ -224,7 +223,6 @@ class port_data(design.design):
|
||||||
offsets=self.bit_offsets,
|
offsets=self.bit_offsets,
|
||||||
words_per_row=self.words_per_row,
|
words_per_row=self.words_per_row,
|
||||||
num_spare_cols=self.num_spare_cols)
|
num_spare_cols=self.num_spare_cols)
|
||||||
self.add_mod(self.sense_amp_array)
|
|
||||||
else:
|
else:
|
||||||
self.sense_amp_array = None
|
self.sense_amp_array = None
|
||||||
|
|
||||||
|
|
@ -236,7 +234,6 @@ class port_data(design.design):
|
||||||
offsets=self.bit_offsets,
|
offsets=self.bit_offsets,
|
||||||
bitcell_bl=self.bl_names[self.port],
|
bitcell_bl=self.bl_names[self.port],
|
||||||
bitcell_br=self.br_names[self.port])
|
bitcell_br=self.br_names[self.port])
|
||||||
self.add_mod(self.column_mux_array)
|
|
||||||
else:
|
else:
|
||||||
self.column_mux_array = None
|
self.column_mux_array = None
|
||||||
|
|
||||||
|
|
@ -248,7 +245,6 @@ class port_data(design.design):
|
||||||
offsets=self.bit_offsets,
|
offsets=self.bit_offsets,
|
||||||
write_size=self.write_size,
|
write_size=self.write_size,
|
||||||
num_spare_cols=self.num_spare_cols)
|
num_spare_cols=self.num_spare_cols)
|
||||||
self.add_mod(self.write_driver_array)
|
|
||||||
if self.write_size is not None:
|
if self.write_size is not None:
|
||||||
# RBLs don't get a write mask
|
# RBLs don't get a write mask
|
||||||
self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
|
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,
|
offsets=self.bit_offsets,
|
||||||
word_size=self.word_size,
|
word_size=self.word_size,
|
||||||
write_size=self.write_size)
|
write_size=self.write_size)
|
||||||
self.add_mod(self.write_mask_and_array)
|
|
||||||
else:
|
else:
|
||||||
self.write_mask_and_array = None
|
self.write_mask_and_array = None
|
||||||
|
|
||||||
|
|
@ -516,9 +511,6 @@ class port_data(design.design):
|
||||||
wdriver_inst = self.write_driver_array_inst
|
wdriver_inst = self.write_driver_array_inst
|
||||||
|
|
||||||
for bit in range(self.num_wmasks):
|
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))
|
wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit))
|
||||||
wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit))
|
wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,17 @@
|
||||||
# (acting for and on behalf of Oklahoma State University)
|
# (acting for and on behalf of Oklahoma State University)
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
import contact
|
from base import design
|
||||||
import design
|
|
||||||
import debug
|
import debug
|
||||||
from pgate import pgate
|
from .pgate import *
|
||||||
from tech import parameter, drc
|
from tech import parameter, drc
|
||||||
from vector import vector
|
from base import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from sram_factory import factory
|
from sram_factory import factory
|
||||||
from tech import cell_properties as cell_props
|
from tech import cell_properties as cell_props
|
||||||
|
|
||||||
|
|
||||||
class precharge(design.design):
|
class precharge(design):
|
||||||
"""
|
"""
|
||||||
Creates a single precharge cell
|
Creates a single precharge cell
|
||||||
This module implements the precharge bitline cell used in the design.
|
This module implements the precharge bitline cell used in the design.
|
||||||
|
|
@ -71,7 +70,7 @@ class precharge(design.design):
|
||||||
self.connect_poly()
|
self.connect_poly()
|
||||||
self.route_en()
|
self.route_en()
|
||||||
self.place_nwell_and_contact()
|
self.place_nwell_and_contact()
|
||||||
self.route_vdd_rail()
|
self.route_supplies()
|
||||||
self.route_bitlines()
|
self.route_bitlines()
|
||||||
self.connect_to_bitlines()
|
self.connect_to_bitlines()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
@ -90,35 +89,19 @@ class precharge(design.design):
|
||||||
width=self.ptx_width,
|
width=self.ptx_width,
|
||||||
mults=self.ptx_mults,
|
mults=self.ptx_mults,
|
||||||
tx_type="pmos")
|
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 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_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
|
self.add_layout_pin_rect_center(text="vdd",
|
||||||
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
|
layer=pmos_pin.layer,
|
||||||
self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos])
|
offset=self.well_contact_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"))
|
|
||||||
|
|
||||||
def create_ptx(self):
|
def create_ptx(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -194,7 +177,7 @@ class precharge(design.design):
|
||||||
pin_offset = self.lower_pmos_inst.get_pin("G").lr()
|
pin_offset = self.lower_pmos_inst.get_pin("G").lr()
|
||||||
# This is an extra space down for some techs with contact to active spacing
|
# This is an extra space down for some techs with contact to active spacing
|
||||||
contact_space = max(self.poly_space,
|
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)
|
offset = pin_offset - vector(0, contact_space)
|
||||||
self.add_via_stack_center(from_layer="poly",
|
self.add_via_stack_center(from_layer="poly",
|
||||||
to_layer=self.en_layer,
|
to_layer=self.en_layer,
|
||||||
|
|
@ -214,7 +197,7 @@ class precharge(design.design):
|
||||||
|
|
||||||
# adds the contact from active to metal1
|
# adds the contact from active to metal1
|
||||||
offset_height = self.upper_pmos1_inst.uy() + \
|
offset_height = self.upper_pmos1_inst.uy() + \
|
||||||
contact.active_contact.height + \
|
self.active_contact.height + \
|
||||||
self.nwell_extend_active
|
self.nwell_extend_active
|
||||||
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
|
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
|
||||||
vector(0, offset_height)
|
vector(0, offset_height)
|
||||||
|
|
@ -226,7 +209,7 @@ class precharge(design.design):
|
||||||
to_layer=self.bitline_layer,
|
to_layer=self.bitline_layer,
|
||||||
offset=self.well_contact_pos)
|
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
|
# nwell should span the whole design since it is pmos only
|
||||||
self.add_rect(layer="nwell",
|
self.add_rect(layer="nwell",
|
||||||
|
|
@ -305,4 +288,3 @@ class precharge(design.design):
|
||||||
self.add_path(self.bitline_layer,
|
self.add_path(self.bitline_layer,
|
||||||
[left_pos, right_pos],
|
[left_pos, right_pos],
|
||||||
width=pmos_pin.height())
|
width=pmos_pin.height())
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue