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
|
||||
on: [push]
|
||||
jobs:
|
||||
scn4me_subm:
|
||||
regress:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
- name: SCMOS test
|
||||
- name: Docker build
|
||||
run: |
|
||||
cd ${{ github.workspace }}/docker
|
||||
make build
|
||||
- name: PDK Install
|
||||
run: |
|
||||
. /home/github-runner/setup-paths.sh
|
||||
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
||||
export OPENRAM_TECH="${{ github.workspace }}/technology:/software/PDKs/skywater-tech"
|
||||
export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp"
|
||||
export OPENRAM_TECH="${{ github.workspace }}/technology"
|
||||
#cd $OPENRAM_HOME/tests
|
||||
#export PDK_ROOT="${{ github.workspace }}/pdk"
|
||||
#make pdk
|
||||
#make install
|
||||
- name: Regress
|
||||
run: |
|
||||
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
||||
export OPENRAM_TECH="${{ github.workspace }}/technology"
|
||||
export FREEPDK45="~/FreePDK45"
|
||||
#cd $OPENRAM_HOME/.. && make pdk && make install
|
||||
#export OPENRAM_TMP="${{ github.workspace }}/scn4me_subm_temp"
|
||||
#python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t scn4m_subm
|
||||
$OPENRAM_HOME/tests/regress.py -j 24 -t scn4m_subm
|
||||
#$OPENRAM_HOME/tests/regress.py -j 24 -t scn4m_subm
|
||||
cd $OPENRAM_HOME/tests
|
||||
make clean
|
||||
make -k -j 48
|
||||
- name: Archive
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: scn4me_subm Archives
|
||||
path: ${{ github.workspace }}/*.zip
|
||||
freepdk45:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
- name: FreePDK45 test
|
||||
run: |
|
||||
. /home/github-runner/setup-paths.sh
|
||||
export OPENRAM_HOME="${{ github.workspace }}/compiler"
|
||||
export OPENRAM_TECH="${{ github.workspace }}/technology:/software/PDKs/skywater-tech"
|
||||
export OPENRAM_TMP="${{ github.workspace }}/freepdk45_temp"
|
||||
#python3-coverage run -p $OPENRAM_HOME/tests/regress.py -j 12 -t freepdk45
|
||||
$OPENRAM_HOME/tests/regress.py -j 24 -t freepdk45
|
||||
- name: Archive
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: FreePDK45 Archives
|
||||
path: ${{ github.workspace }}/*.zip
|
||||
# coverage_stats:
|
||||
# if: ${{ always() }}
|
||||
# needs: [scn4me_subm, freepdk45]
|
||||
# runs-on: self-hosted
|
||||
# steps:
|
||||
# - name: Coverage stats
|
||||
# run: |
|
||||
# python3-coverage combine
|
||||
# python3-coverage report
|
||||
# python3-coverage html -d ${{ github.workspace }}/coverage_html
|
||||
# - name: Archive coverage
|
||||
# uses: actions/upload-artifact@v2
|
||||
# with:
|
||||
# name: code-coverage-report
|
||||
# path: ${{ github.workspace }}/coverage_html/
|
||||
name: Regress Archives
|
||||
path: ${{ github.workspace }}/compiler/tests/results/*
|
||||
|
|
|
|||
160
Makefile
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))))
|
||||
include $(TOP_DIR)/openram.mk
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
.DEFAULT_GOAL := install
|
||||
|
||||
# Skywater PDK SRAM library
|
||||
#SRAM_LIBRARY ?= $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_bd_sram
|
||||
SRAM_GIT_REPO ?= git@github.com:google/skywater-pdk-libs-sky130_fd_bd_sram.git
|
||||
SRAM_LIBRARY ?= $(TOP_DIR)/sky130_fd_bd_sram
|
||||
# Open PDKs
|
||||
OPEN_PDKS ?= $(PDK_ROOT)/sky130A
|
||||
SRAM_LIB_DIR ?= $(PDK_ROOT)/sky130_fd_bd_sram
|
||||
# Use this for release
|
||||
SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git
|
||||
# Use this for development
|
||||
#SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git
|
||||
#SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
|
||||
SRAM_LIB_GIT_COMMIT ?= a83b6468c48434d927b90058b22047843c58027b
|
||||
|
||||
# Open PDKs
|
||||
OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks
|
||||
OPEN_PDKS_GIT_REPO ?= https://github.com/RTimothyEdwards/open_pdks.git
|
||||
OPEN_PDKS_GIT_COMMIT ?= 1.0.311
|
||||
#OPEN_PDKS_GIT_COMMIT ?= 7ea416610339d3c29af9d0d748ceadd3fd368608
|
||||
SKY130_PDK ?= $(PDK_ROOT)/sky130A
|
||||
|
||||
# Skywater PDK
|
||||
SKY130_PDKS_DIR ?= $(PDK_ROOT)/skywater-pdk
|
||||
SKY130_PDKS_GIT_REPO ?= https://github.com/google/skywater-pdk.git
|
||||
SKY130_PDKS_GIT_COMMIT ?= f70d8ca46961ff92719d8870a18a076370b85f6c
|
||||
|
||||
# Create lists of all the files to copy/link
|
||||
GDS_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.gds))
|
||||
MAG_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.mag))
|
||||
GDS_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.gds))
|
||||
GDS_FILES := $(GDS_FILES) $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/dlxtn/sky130_fd_sc_hd__dlxtn_1.gds
|
||||
MAG_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.mag))
|
||||
|
||||
SPICE_SUFFIX := spice
|
||||
SPICE_LVS_SUFFIX := lvs.$(SPICE_SUFFIX)
|
||||
SPICE_CALIBRE_SUFFIX := lvs.calibre.$(SPICE_SUFFIX)
|
||||
SPICE_KLAYOUT_SUFFIX := lvs.klayout.$(SPICE_SUFFIX)
|
||||
SPICE_BASE_SUFFIX := base.$(SPICE_SUFFIX)
|
||||
ALL_SPICE_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.$(SPICE_SUFFIX)))
|
||||
ALL_SPICE_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.$(SPICE_SUFFIX)))
|
||||
ALL_SPICE_FILES := $(ALL_SPICE_FILES) $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/dlxtn/sky130_fd_sc_hd__dlxtn_1.spice
|
||||
|
||||
MAGLEF_SUFFIX := maglef
|
||||
MAGLEF_FILES := $(sort $(wildcard $(SRAM_LIBRARY)/cells/*/*.$(MAGLEF_SUFFIX)))
|
||||
MAGLEF_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.$(MAGLEF_SUFFIX)))
|
||||
|
||||
MAGICRC_FILE := $(OPEN_PDKS)/libs.tech/magic/sky130A.magicrc
|
||||
MAGICRC_FILE := $(SKY130_PDK)/libs.tech/magic/sky130A.magicrc
|
||||
|
||||
ALL_FILES := $(ALL_SPICE_FILES) $(GDS_FILES) $(MAG_FILES) $(MAGLEF_FILES)
|
||||
|
||||
|
||||
INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib lef_lib maglef_lib
|
||||
INSTALL_BASE_DIRS := gds_lib mag_lib sp_lib lvs_lib calibre_lvs_lib klayout_lvs_lib maglef_lib
|
||||
INSTALL_BASE := $(OPENRAM_HOME)/../technology/sky130
|
||||
INSTALL_DIRS := $(addprefix $(INSTALL_BASE)/,$(INSTALL_BASE_DIRS))
|
||||
|
||||
check-pdk-root:
|
||||
ifndef PDK_ROOT
|
||||
$(error PDK_ROOT is undefined, please export it before running make)
|
||||
endif
|
||||
|
||||
install: $(INSTALL_DIRS)
|
||||
$(SKY130_PDKS_DIR): check-pdk-root
|
||||
@echo "Cloning skywater PDK..."
|
||||
@[ -d $(PDK_ROOT)/skywater-pdk ] || \
|
||||
git clone https://github.com/google/skywater-pdk.git $(PDK_ROOT)/skywater-pdk
|
||||
@cd $(SKY130_PDKS_DIR) && \
|
||||
git checkout main && git pull && \
|
||||
git checkout -qf $(SKY130_PDKS_GIT_COMMIT) && \
|
||||
git submodule update --init libraries/sky130_fd_pr/latest libraries/sky130_fd_sc_hd/latest
|
||||
|
||||
$(SRAM_LIBRARY):
|
||||
git clone $(SRAM_GIT_REPO) $(SRAM_LIBRARY)
|
||||
$(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR)
|
||||
@echo "Cloning open_pdks..."
|
||||
@[ -d $(OPEN_PDKS_DIR) ] || \
|
||||
git clone $(OPEN_PDKS_GIT_REPO) $(OPEN_PDKS_DIR)
|
||||
@cd $(OPEN_PDKS_DIR) && git pull && git checkout $(OPEN_PDKS_GIT_COMMIT)
|
||||
|
||||
.PHONY: $(SRAM_LIBRARY) $(INSTALL_DIRS) install
|
||||
$(SKY130_PDK): $(OPEN_PDKS_DIR) $(SKY130_PDKS_DIR)
|
||||
@echo "Installing open_pdks..."
|
||||
$(DOCKER_CMD) sh -c ". /home/cad-user/.bashrc && cd /pdk/open_pdks && \
|
||||
./configure --enable-sky130-pdk=/pdk/skywater-pdk/libraries --with-sky130-local-path=/pdk && \
|
||||
cd sky130 && \
|
||||
make veryclean && \
|
||||
make && \
|
||||
make SHARED_PDKS_PATH=/pdk install"
|
||||
|
||||
all: $(SRAM_LIBRARY)
|
||||
$(SRAM_LIB_DIR): check-pdk-root
|
||||
@echo "Cloning SRAM library..."
|
||||
@[ -d $(SRAM_LIB_DIR) ] || (\
|
||||
git clone $(SRAM_LIB_GIT_REPO) $(SRAM_LIB_DIR) && \
|
||||
cd $(SRAM_LIB_DIR) && git pull && git checkout $(SRAM_LIB_GIT_COMMIT))
|
||||
|
||||
install: $(SRAM_LIB_DIR) pdk
|
||||
@[ -d $(PDK_ROOT)/sky130A ] || \
|
||||
(echo "Warning: $(PDK_ROOT)/sky130A not found!! Run make pdk first." && false)
|
||||
@[ -d $(PDK_ROOT)/skywater-pdk ] || \
|
||||
(echo "Warning: $(PDK_ROOT)/skywater-pdk not found!! Run make pdk first." && false)
|
||||
@echo "Installing sky130 SRAM PDK..."
|
||||
@echo "PDK_ROOT='$(PDK_ROOT)'"
|
||||
@echo "SRAM_LIBRARY='$(SRAM_LIBRARY)'"
|
||||
@echo "OPEN_PDKS='$(OPEN_PDKS)'"
|
||||
make install
|
||||
@echo "SRAM_LIB_DIR='$(SRAM_LIB_DIR)'"
|
||||
@echo "SKY130_PDK='$(SKY130_PDK)'"
|
||||
@make $(INSTALL_DIRS)
|
||||
.PHONY: install
|
||||
|
||||
pdk: $(SKY130_PDK)
|
||||
@true
|
||||
.PHONY: pdk
|
||||
|
||||
$(INSTALL_BASE)/gds_lib: $(GDS_FILES)
|
||||
@echo
|
||||
@echo "Setting up GDS cell library for OpenRAM."
|
||||
@echo "=================================================================="
|
||||
mkdir -p $@
|
||||
@cp -va $? $@
|
||||
cp -va $? $@
|
||||
@echo "=================================================================="
|
||||
@echo
|
||||
|
||||
|
|
@ -124,6 +157,18 @@ $(INSTALL_BASE)/calibre_lvs_lib: $(filter %.$(SPICE_CALIBRE_SUFFIX),$(ALL_SPICE_
|
|||
@echo "=================================================================="
|
||||
@echo
|
||||
|
||||
$(INSTALL_BASE)/klayout_lvs_lib: $(filter %.$(SPICE_KLAYOUT_SUFFIX),$(ALL_SPICE_FILES))
|
||||
@echo
|
||||
@echo "Setting up klayout LVS library for OpenRAM."
|
||||
@echo "=================================================================="
|
||||
mkdir -p $@
|
||||
@for SP in $?; do \
|
||||
cp -va $$SP $@/$$(basename $$SP .$(SPICE_KLAYOUT_SUFFIX)).sp; \
|
||||
done
|
||||
@echo "=================================================================="
|
||||
@echo
|
||||
|
||||
|
||||
$(INSTALL_BASE)/sp_lib: $(filter-out %.$(SPICE_LVS_SUFFIX) %.$(SPICE_CALIBRE_SUFFIX),$(ALL_SPICE_FILES))
|
||||
@echo
|
||||
@echo "Setting up spice simulation library for OpenRAM."
|
||||
|
|
@ -140,10 +185,33 @@ $(INSTALL_BASE)/sp_lib: $(filter-out %.$(SPICE_LVS_SUFFIX) %.$(SPICE_CALIBRE_SUF
|
|||
@echo "=================================================================="
|
||||
@echo
|
||||
|
||||
macros:
|
||||
cd macros && make
|
||||
|
||||
.PHONY: macros
|
||||
|
||||
clean:
|
||||
rm -f $(SRAM_LIBRARY)
|
||||
rm -f $(INSTALL_BASE)/tech/.magicrc
|
||||
rm -f $(INSTALL_BASE)/mag_lib/.magicrc
|
||||
rm -f $(INSTALL_BASE)/lef_lib/.magicrc
|
||||
rm -f $(INSTALL_BASE)/maglef_lib/.magicrc
|
||||
rm -rf $(INSTALL_DIRS)
|
||||
@rm -f *.zip
|
||||
.PHONE: clean
|
||||
|
||||
uninstall: clean
|
||||
@rm -f $(INSTALL_BASE)/tech/.magicrc
|
||||
@rm -f $(INSTALL_BASE)/mag_lib/.magicrc
|
||||
@rm -f $(INSTALL_BASE)/lef_lib/.magicrc
|
||||
@rm -f $(INSTALL_BASE)/maglef_lib/.magicrc
|
||||
@rm -rf $(INSTALL_DIRS)
|
||||
.PHONY: uninstall
|
||||
|
||||
# wipe the entire repos
|
||||
wipe: uninstall
|
||||
@echo $(SKY130_PDK)
|
||||
@echo $(SRAM_LIB_DIR)
|
||||
@echo $(OPEN_PDKS_DIR)
|
||||
@echo $(SKY130_PDKS_DIR)
|
||||
@echo "Wiping above PDK repos in 5 sec... (ctrl-c to quit)"
|
||||
@sleep 5
|
||||
@rm -rf $(SKY130_PDK)
|
||||
@rm -rf $(SRAM_LIB_DIR)
|
||||
@rm -rf $(OPEN_PDKS_DIR)
|
||||
@rm -rf $(SKY130_PDKS_DIR)
|
||||
.PHONY: wipe
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
# Porting to a New Technology
|
||||
|
||||
If you want to support a new technology, you will need to create:
|
||||
+ a setup script for each technology you want to use
|
||||
+ a technology directory for each technology with the base cells
|
||||
|
||||
We provide two technology examples for [SCMOS] and [FreePDK45]. Each
|
||||
specific technology (e.g., [FreePDK45]) should be a subdirectory
|
||||
(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files:
|
||||
* gds_lib folder with all the .gds (premade) library cells:
|
||||
* dff.gds
|
||||
* sense_amp.gds
|
||||
* write_driver.gds
|
||||
* cell_1rw.gds
|
||||
* replica\_cell\_1rw.gds
|
||||
* dummy\_cell\_1rw.gds
|
||||
* sp_lib folder with all the .sp (premade) library netlists for the above cells.
|
||||
* layers.map
|
||||
* A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with:
|
||||
* References in tech.py to spice models
|
||||
* DRC/LVS rules needed for dynamic cells and routing
|
||||
* Layer information
|
||||
* Spice and supply information
|
||||
* etc.
|
||||
150
README.md
150
README.md
|
|
@ -28,23 +28,32 @@ things that need to be fixed.
|
|||
|
||||
## Dependencies
|
||||
|
||||
The OpenRAM compiler has very few dependencies:
|
||||
+ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later) or [Xyce] 7.2 (or later)
|
||||
+ Python 3.5 or higher
|
||||
Please see the Dockerfile for the required versions of tools.
|
||||
|
||||
In general, the OpenRAM compiler has very few dependencies:
|
||||
+ Docker
|
||||
+ Make
|
||||
+ Python 3.6 or higher
|
||||
+ Various Python packages (pip install -r requirements.txt)
|
||||
+ [Git]
|
||||
|
||||
If you want to perform DRC and LVS, you will need either:
|
||||
+ Calibre (for [FreePDK45])
|
||||
+ [Magic] 8.3.130 or newer
|
||||
+ [Netgen] 1.5.164 or newer
|
||||
## Docker
|
||||
|
||||
We have a [docker setup](./docker) to run OpenRAM. To use this, you should run:
|
||||
```
|
||||
cd openram/docker
|
||||
make build
|
||||
```
|
||||
This must be run once and will take a while to build all the tools.
|
||||
|
||||
You must set two environment variables:
|
||||
+ OPENRAM\_HOME should point to the compiler source directory.
|
||||
+ OPENERAM\_TECH should point to one or more root technology directories (colon separated).
|
||||
|
||||
## Environment
|
||||
|
||||
You must set two environment variables:
|
||||
+ OPENRAM\_HOME should point to the compiler source directory.
|
||||
+ OPENERAM\_TECH should point to one or more root technology directories (colon separated).
|
||||
|
||||
|
||||
For example add this to your .bashrc:
|
||||
|
||||
```
|
||||
|
|
@ -52,34 +61,44 @@ For example add this to your .bashrc:
|
|||
export OPENRAM_TECH="$HOME/openram/technology"
|
||||
```
|
||||
|
||||
You may also wish to add OPENRAM\_HOME to your PYTHONPATH:
|
||||
You should also add OPENRAM\_HOME to your PYTHONPATH:
|
||||
|
||||
```
|
||||
export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME"
|
||||
export PYTHONPATH=$OPENRAM_HOME
|
||||
```
|
||||
Note that if you want symbols to resolve in your editor, you may also want to add the specific technology
|
||||
directory that you use and any custom technology modules as well. For example:
|
||||
```
|
||||
export PYTHONPATH="$OPENRAM_HOME:$OPENRAM_TECH/sky130:$OPENRAM_TECH/sky130/custom"
|
||||
```
|
||||
|
||||
We include the tech files necessary for [SCMOS] SCN4M_SUBM. The
|
||||
[SCMOS] spice models, however, are generic and should be replaced with
|
||||
foundry models. If you are using [FreePDK45], you should also have
|
||||
that set up and have the environment variable point to the PDK. For
|
||||
example add this to your .bashrc:
|
||||
We include the tech files necessary for [SCMOS] SCN4M_SUBM,
|
||||
[FreePDK45]. The [SCMOS] spice models, however, are
|
||||
generic and should be replaced with foundry models. You may get the
|
||||
entire [FreePDK45 PDK here][FreePDK45].
|
||||
|
||||
```
|
||||
export FREEPDK45="/bsoe/software/design-kits/FreePDK45"
|
||||
```
|
||||
|
||||
You may get the entire [FreePDK45 PDK here][FreePDK45].
|
||||
If you are using [SCMOS], you should install [Magic] and [Netgen].
|
||||
We have included the most recent SCN4M_SUBM design rules from [Qflow].
|
||||
### Sky130 Setup
|
||||
|
||||
To install [Sky130], you must have the open_pdks files installed in $PDK_ROOT.
|
||||
To install this automatically, you can run:
|
||||
|
||||
cd $HOME/openram
|
||||
make pdk
|
||||
|
||||
Then you must also install the [Sky130] SRAM build space and the appropriate cell views
|
||||
by running:
|
||||
|
||||
cd $HOME/openram
|
||||
make install
|
||||
|
||||
# Basic Usage
|
||||
|
||||
Once you have defined the environment, you can run OpenRAM from the command line
|
||||
using a single configuration file written in Python.
|
||||
Once you have defined the environment, you can run OpenRAM from the command line
|
||||
using a single configuration file written in Python.
|
||||
|
||||
For example, create a file called *myconfig.py* specifying the following
|
||||
parameters for your memory:
|
||||
|
||||
```
|
||||
# Data word size
|
||||
word_size = 2
|
||||
|
|
@ -116,60 +135,55 @@ python3 $OPENRAM_HOME/openram.py myconfig
|
|||
You can see all of the options for the configuration file in
|
||||
$OPENRAM\_HOME/options.py
|
||||
|
||||
To run designs in Docker, it is suggested to use, for example:
|
||||
```
|
||||
cd openram/macros
|
||||
make example_config_scn4m_subm
|
||||
```
|
||||
|
||||
# Unit Tests
|
||||
|
||||
Regression testing performs a number of tests for all modules in OpenRAM.
|
||||
From the unit test directory ($OPENRAM\_HOME/tests),
|
||||
From the unit test directory ($OPENRAM\_HOME/tests),
|
||||
use the following command to run all regression tests:
|
||||
|
||||
```
|
||||
python3 regress.py
|
||||
cd openram/compiler/tests
|
||||
make -j 3
|
||||
```
|
||||
To run a specific test:
|
||||
The -j can run with 3 threads. By default, this will run in all technologies.
|
||||
|
||||
To run a specific test in all technologies:
|
||||
```
|
||||
python3 {unit test}.py
|
||||
cd openram/compiler/tests
|
||||
make 05_bitcell_array_test
|
||||
```
|
||||
The unit tests take the same arguments as openram.py itself.
|
||||
|
||||
To increase the verbosity of the test, add one (or more) -v options:
|
||||
To run a specific technology:
|
||||
```
|
||||
python3 tests/00_code_format_check_test.py -v -t freepdk45
|
||||
cd openram/compiler/tests
|
||||
TECHS=scn4m_subm make 05_bitcell_array_test
|
||||
```
|
||||
To specify a particular technology use "-t <techname>" such as
|
||||
"-t freepdk45". The default for a unit test is scn4m_subm.
|
||||
The default for openram.py is specified in the configuration file.
|
||||
|
||||
To increase the verbosity of the test, add one (or more) -v options and
|
||||
pass it as an argument to OpenRAM:
|
||||
```
|
||||
ARGS="-v" make 05_bitcell_array_test
|
||||
```
|
||||
|
||||
# Porting to a New Technology
|
||||
|
||||
If you want to support a new technology, you will need to create:
|
||||
+ a setup script for each technology you want to use
|
||||
+ a technology directory for each technology with the base cells
|
||||
|
||||
We provide two technology examples for [SCMOS] and [FreePDK45]. Each
|
||||
specific technology (e.g., [FreePDK45]) should be a subdirectory
|
||||
(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files:
|
||||
* gds_lib folder with all the .gds (premade) library cells:
|
||||
* dff.gds
|
||||
* sense_amp.gds
|
||||
* write_driver.gds
|
||||
* cell_1rw.gds
|
||||
* replica\_cell\_1rw.gds
|
||||
* dummy\_cell\_1rw.gds
|
||||
* sp_lib folder with all the .sp (premade) library netlists for the above cells.
|
||||
* layers.map
|
||||
* A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with:
|
||||
* References in tech.py to spice models
|
||||
* DRC/LVS rules needed for dynamic cells and routing
|
||||
* Layer information
|
||||
* Spice and supply information
|
||||
* etc.
|
||||
Unit test results are put in a directory:
|
||||
```
|
||||
openram/compiler/tests/results/<technology>/<test>
|
||||
```
|
||||
If the test fails, there will be a tmp directory with intermediate results.
|
||||
If the test passes, this directory will be deleted to save space.
|
||||
You can view the .out file to see what the output of a test is in either case.
|
||||
|
||||
# Get Involved
|
||||
|
||||
+ [Port it](./PORTING.md) to a new technology.
|
||||
+ Report bugs by submitting [Github issues].
|
||||
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
|
||||
+ Submit code/fixes using a [Github pull request]
|
||||
+ Submit code/fixes using a [Github pull request]
|
||||
+ Follow our [project][Github project].
|
||||
+ Read and cite our [ICCAD paper][OpenRAMpaper]
|
||||
|
||||
|
|
@ -180,8 +194,9 @@ specific technology (e.g., [FreePDK45]) should be a subdirectory
|
|||
+ [OpenRAM Slack Workspace][Slack]
|
||||
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
||||
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
|
||||
+ <a rel="me" href="https://fosstodon.org/@mrg">@mrg@fostodon.org</a>
|
||||
|
||||
# License
|
||||
# License
|
||||
|
||||
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
|
||||
|
||||
|
|
@ -203,7 +218,7 @@ If I forgot to add you, please let me know!
|
|||
|
||||
[Github issues]: https://github.com/VLSIDA/OpenRAM/issues
|
||||
[Github pull request]: https://github.com/VLSIDA/OpenRAM/pulls
|
||||
[Github project]: https://github.com/VLSIDA/OpenRAM
|
||||
[Github project]: https://github.com/VLSIDA/OpenRAM
|
||||
|
||||
[documentation]: https://docs.google.com/presentation/d/10InGB33N51I6oBHnqpU7_w9DXlx-qe9zdrlco2Yc5co/edit?usp=sharing
|
||||
[dev-group]: mailto:openram-dev-group@ucsc.edu
|
||||
|
|
@ -211,6 +226,7 @@ If I forgot to add you, please let me know!
|
|||
[dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu
|
||||
[user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu
|
||||
|
||||
[Klayout]: https://www.klayout.de/
|
||||
[Magic]: http://opencircuitdesign.com/magic/
|
||||
[Netgen]: http://opencircuitdesign.com/netgen/
|
||||
[Qflow]: http://opencircuitdesign.com/qflow/history.html
|
||||
|
|
@ -218,8 +234,10 @@ If I forgot to add you, please let me know!
|
|||
[Xyce]: http://xyce.sandia.gov/
|
||||
[Git]: https://git-scm.com/
|
||||
|
||||
[OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/
|
||||
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
|
||||
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
|
||||
[Sky130]: https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
|
||||
|
||||
[Slack]: https://join.slack.com/t/openram/shared_invite/zt-onim74ue-zlttW5XI30xvdBlJGJF6JA
|
||||
|
||||
|
||||
[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LWZiYWMwNjNkZThmYTdkODc3NDE1NDhjNzUxNDhmMDQ4ZTM3NDgwNWFlNjM5NWFiZDkyMzBlNzc1NTg3ZjllNTY
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ ${CELL_TESTS} \
|
|||
${MODULE_TESTS} \
|
||||
${TOP_TESTS} \
|
||||
${CHAR_TESTS} \
|
||||
${USAGE_TESTS}
|
||||
${USAGE_TESTS}
|
||||
|
||||
.PHONY: ${ALL_TESTS}
|
||||
|
||||
|
|
@ -64,8 +64,8 @@ usage: ${USAGE_TESTS}
|
|||
|
||||
$(ALL_TESTS):
|
||||
python3 $@ -t ${TECH}
|
||||
|
||||
|
||||
|
||||
|
||||
OPENRAM_TECHS = $(subst :, ,$(OPENRAM_TECH))
|
||||
TECH_DIR := $(word 1, $(foreach dir,$(OPENRAM_TECHS),$(wildcard $(dir)/$(TECH))))
|
||||
CONFIG_DIR = $(OPENRAM_HOME)/model_configs
|
||||
|
|
@ -75,9 +75,9 @@ CSV_DIR = $(TECH_DIR)/sim_data
|
|||
# Creates names of technology specific okay files for the configs
|
||||
STAMPS=$(addprefix $(SIM_DIR)/, $(addsuffix .ok, $(notdir $(basename $(MODEL_CONFIGS)))))
|
||||
|
||||
OPTS =
|
||||
OPTS =
|
||||
# Characterize and perform DRC/LVS
|
||||
OPTS += -c
|
||||
OPTS += -c
|
||||
# Do not characterize or perform DRC/LVS
|
||||
OPTS += -n
|
||||
# Verbosity
|
||||
|
|
@ -100,8 +100,7 @@ model: $(STAMPS)
|
|||
|
||||
clean_model:
|
||||
rm -f -r $(SIM_DIR)/*.ok
|
||||
|
||||
|
||||
clean:
|
||||
find . -name \*.pyc -exec rm {} \;
|
||||
find . -name \*~ -exec rm {} \;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 debug
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
import design
|
||||
from .vector import vector
|
||||
from .design import design
|
||||
|
||||
|
||||
class channel_net():
|
||||
|
|
@ -75,7 +75,7 @@ class channel_net():
|
|||
return min_overlap or max_overlap
|
||||
|
||||
|
||||
class channel_route(design.design):
|
||||
class channel_route(design):
|
||||
|
||||
unique_id = 0
|
||||
|
||||
|
|
@ -242,12 +242,12 @@ class channel_route(design.design):
|
|||
if self.vertical:
|
||||
self.add_vertical_trunk_route(net.pins,
|
||||
current_offset,
|
||||
self.vertical_nonpref_pitch)
|
||||
self.horizontal_pitch)
|
||||
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
|
||||
else:
|
||||
self.add_horizontal_trunk_route(net.pins,
|
||||
current_offset,
|
||||
self.horizontal_nonpref_pitch)
|
||||
self.vertical_pitch)
|
||||
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
|
||||
|
||||
# Remove the net from other constriants in the VCG
|
||||
|
|
|
|||
|
|
@ -5,16 +5,14 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import hierarchy_design
|
||||
import debug
|
||||
from tech import drc, layer
|
||||
import tech
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
import sys
|
||||
from .hierarchy_design import hierarchy_design
|
||||
from .vector import vector
|
||||
from tech import drc, layer, preferred_directions
|
||||
from tech import layer as tech_layers
|
||||
|
||||
|
||||
class contact(hierarchy_design.hierarchy_design):
|
||||
class contact(hierarchy_design):
|
||||
"""
|
||||
Object for a contact shape with its conductor enclosures. Creates
|
||||
a contact array minimum active or poly enclosure and metal1
|
||||
|
|
@ -51,20 +49,20 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
|
||||
# Non-preferred directions
|
||||
if directions == "nonpref":
|
||||
first_dir = "H" if tech.preferred_directions[layer_stack[0]]=="V" else "V"
|
||||
second_dir = "H" if tech.preferred_directions[layer_stack[2]]=="V" else "V"
|
||||
first_dir = "H" if preferred_directions[layer_stack[0]]=="V" else "V"
|
||||
second_dir = "H" if preferred_directions[layer_stack[2]]=="V" else "V"
|
||||
self.directions = (first_dir, second_dir)
|
||||
# Preferred directions
|
||||
elif directions == "pref":
|
||||
self.directions = (tech.preferred_directions[layer_stack[0]],
|
||||
tech.preferred_directions[layer_stack[2]])
|
||||
self.directions = (preferred_directions[layer_stack[0]],
|
||||
preferred_directions[layer_stack[2]])
|
||||
# User directions
|
||||
elif directions:
|
||||
self.directions = directions
|
||||
# Preferred directions
|
||||
else:
|
||||
self.directions = (tech.preferred_directions[layer_stack[0]],
|
||||
tech.preferred_directions[layer_stack[2]])
|
||||
self.directions = (preferred_directions[layer_stack[0]],
|
||||
preferred_directions[layer_stack[2]])
|
||||
self.offset = vector(0, 0)
|
||||
self.implant_type = implant_type
|
||||
self.well_type = well_type
|
||||
|
|
@ -101,7 +99,7 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
self.second_layer_name = second_layer
|
||||
|
||||
# Contacts will have unique per first layer
|
||||
if via_layer in tech.layer:
|
||||
if via_layer in tech_layers:
|
||||
self.via_layer_name = via_layer
|
||||
elif via_layer == "contact":
|
||||
if first_layer in ("active", "poly"):
|
||||
|
|
@ -194,7 +192,7 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
def create_nitride_cut_enclosure(self):
|
||||
""" Special layer that encloses poly contacts in some processes """
|
||||
# Check if there is a special poly nitride cut layer
|
||||
if "npc" not in tech.layer:
|
||||
if "npc" not in tech_layers:
|
||||
return
|
||||
|
||||
npc_enclose_poly = drc("npc_enclose_poly")
|
||||
|
|
@ -256,7 +254,7 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
|
||||
# Optionally implant well if layer exists
|
||||
well_layer = "{}well".format(self.well_type)
|
||||
if well_layer in tech.layer:
|
||||
if well_layer in tech_layers:
|
||||
well_width_rule = drc("minwidth_" + well_layer)
|
||||
self.well_enclose_active = drc(well_layer + "_enclose_active")
|
||||
self.well_width = max(self.first_layer_width + 2 * self.well_enclose_active,
|
||||
|
|
@ -275,33 +273,3 @@ class contact(hierarchy_design.hierarchy_design):
|
|||
return self.return_power()
|
||||
|
||||
|
||||
# Set up a static for each layer to be used for measurements
|
||||
for layer_stack in tech.layer_stacks:
|
||||
(layer1, via, layer2) = layer_stack
|
||||
cont = factory.create(module_type="contact",
|
||||
layer_stack=layer_stack)
|
||||
module = sys.modules[__name__]
|
||||
# Also create a contact that is just the first layer
|
||||
if layer1 == "poly" or layer1 == "active":
|
||||
setattr(module, layer1 + "_contact", cont)
|
||||
else:
|
||||
setattr(module, layer1 + "_via", cont)
|
||||
|
||||
# Set up a static for each well contact for measurements
|
||||
if "nwell" in tech.layer:
|
||||
cont = factory.create(module_type="contact",
|
||||
layer_stack=tech.active_stack,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
module = sys.modules[__name__]
|
||||
setattr(module, "nwell_contact", cont)
|
||||
|
||||
if "pwell" in tech.layer:
|
||||
cont = factory.create(module_type="contact",
|
||||
layer_stack=tech.active_stack,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
module = sys.modules[__name__]
|
||||
setattr(module, "pwell_contact", cont)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,13 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from hierarchy_design import hierarchy_design
|
||||
import utils
|
||||
import contact
|
||||
import debug
|
||||
from tech import GDS, layer
|
||||
from tech import preferred_directions
|
||||
from tech import cell_properties as props
|
||||
from globals import OPTS
|
||||
import re
|
||||
import debug
|
||||
from . import utils
|
||||
from .hierarchy_design import hierarchy_design
|
||||
|
||||
|
||||
class design(hierarchy_design):
|
||||
|
|
@ -68,207 +66,20 @@ class design(hierarchy_design):
|
|||
|
||||
self.setup_multiport_constants()
|
||||
|
||||
try:
|
||||
from tech import power_grid
|
||||
self.supply_stack = power_grid
|
||||
except ImportError:
|
||||
# if no power_grid is specified by tech we use sensible defaults
|
||||
# Route a M3/M4 grid
|
||||
self.supply_stack = self.m3_stack
|
||||
|
||||
def check_pins(self):
|
||||
for pin_name in self.pins:
|
||||
pins = self.get_pins(pin_name)
|
||||
for pin in pins:
|
||||
print(pin_name, pin)
|
||||
|
||||
@classmethod
|
||||
def setup_drc_constants(design):
|
||||
"""
|
||||
These are some DRC constants used in many places
|
||||
in the compiler.
|
||||
"""
|
||||
# Make some local rules for convenience
|
||||
from tech import drc
|
||||
for rule in drc.keys():
|
||||
# Single layer width rules
|
||||
match = re.search(r"minwidth_(.*)", rule)
|
||||
if match:
|
||||
if match.group(1) == "active_contact":
|
||||
setattr(design, "contact_width", drc(match.group(0)))
|
||||
else:
|
||||
setattr(design, match.group(1) + "_width", drc(match.group(0)))
|
||||
|
||||
# Single layer area rules
|
||||
match = re.search(r"minarea_(.*)", rule)
|
||||
if match:
|
||||
setattr(design, match.group(0), drc(match.group(0)))
|
||||
|
||||
# Single layer spacing rules
|
||||
match = re.search(r"(.*)_to_(.*)", rule)
|
||||
if match and match.group(1) == match.group(2):
|
||||
setattr(design, match.group(1) + "_space", drc(match.group(0)))
|
||||
elif match and match.group(1) != match.group(2):
|
||||
if match.group(2) == "poly_active":
|
||||
setattr(design, match.group(1) + "_to_contact",
|
||||
drc(match.group(0)))
|
||||
else:
|
||||
setattr(design, match.group(0), drc(match.group(0)))
|
||||
|
||||
match = re.search(r"(.*)_enclose_(.*)", rule)
|
||||
if match:
|
||||
setattr(design, match.group(0), drc(match.group(0)))
|
||||
|
||||
match = re.search(r"(.*)_extend_(.*)", rule)
|
||||
if match:
|
||||
setattr(design, match.group(0), drc(match.group(0)))
|
||||
|
||||
# Create the maximum well extend active that gets used
|
||||
# by cells to extend the wells for interaction with other cells
|
||||
from tech import layer
|
||||
design.well_extend_active = 0
|
||||
if "nwell" in layer:
|
||||
design.well_extend_active = max(design.well_extend_active, design.nwell_extend_active)
|
||||
if "pwell" in layer:
|
||||
design.well_extend_active = max(design.well_extend_active, design.pwell_extend_active)
|
||||
|
||||
# The active offset is due to the well extension
|
||||
if "pwell" in layer:
|
||||
design.pwell_enclose_active = drc("pwell_enclose_active")
|
||||
else:
|
||||
design.pwell_enclose_active = 0
|
||||
if "nwell" in layer:
|
||||
design.nwell_enclose_active = drc("nwell_enclose_active")
|
||||
else:
|
||||
design.nwell_enclose_active = 0
|
||||
# Use the max of either so that the poly gates will align properly
|
||||
design.well_enclose_active = max(design.pwell_enclose_active,
|
||||
design.nwell_enclose_active,
|
||||
design.active_space)
|
||||
|
||||
# These are for debugging previous manual rules
|
||||
if False:
|
||||
print("poly_width", design.poly_width)
|
||||
print("poly_space", design.poly_space)
|
||||
print("m1_width", design.m1_width)
|
||||
print("m1_space", design.m1_space)
|
||||
print("m2_width", design.m2_width)
|
||||
print("m2_space", design.m2_space)
|
||||
print("m3_width", design.m3_width)
|
||||
print("m3_space", design.m3_space)
|
||||
print("m4_width", design.m4_width)
|
||||
print("m4_space", design.m4_space)
|
||||
print("active_width", design.active_width)
|
||||
print("active_space", design.active_space)
|
||||
print("contact_width", design.contact_width)
|
||||
print("poly_to_active", design.poly_to_active)
|
||||
print("poly_extend_active", design.poly_extend_active)
|
||||
print("poly_to_contact", design.poly_to_contact)
|
||||
print("active_contact_to_gate", design.active_contact_to_gate)
|
||||
print("poly_contact_to_gate", design.poly_contact_to_gate)
|
||||
print("well_enclose_active", design.well_enclose_active)
|
||||
print("implant_enclose_active", design.implant_enclose_active)
|
||||
print("implant_space", design.implant_space)
|
||||
import sys
|
||||
sys.exit(1)
|
||||
|
||||
@classmethod
|
||||
def setup_layer_constants(design):
|
||||
"""
|
||||
These are some layer constants used
|
||||
in many places in the compiler.
|
||||
"""
|
||||
|
||||
from tech import layer_indices
|
||||
import tech
|
||||
for layer_id in layer_indices:
|
||||
key = "{}_stack".format(layer_id)
|
||||
|
||||
# Set the stack as a local helper
|
||||
try:
|
||||
layer_stack = getattr(tech, key)
|
||||
setattr(design, key, layer_stack)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Skip computing the pitch for non-routing layers
|
||||
if layer_id in ["active", "nwell"]:
|
||||
continue
|
||||
|
||||
# Add the pitch
|
||||
setattr(design,
|
||||
"{}_pitch".format(layer_id),
|
||||
design.compute_pitch(layer_id, True))
|
||||
|
||||
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
||||
setattr(design,
|
||||
"{}_nonpref_pitch".format(layer_id),
|
||||
design.compute_pitch(layer_id, False))
|
||||
|
||||
if False:
|
||||
from tech import preferred_directions
|
||||
print(preferred_directions)
|
||||
from tech import layer_indices
|
||||
for name in layer_indices:
|
||||
if name == "active":
|
||||
continue
|
||||
try:
|
||||
print("{0} width {1} space {2}".format(name,
|
||||
getattr(design, "{}_width".format(name)),
|
||||
getattr(design, "{}_space".format(name))))
|
||||
|
||||
print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)),
|
||||
getattr(design, "{}_nonpref_pitch".format(name))))
|
||||
except AttributeError:
|
||||
pass
|
||||
import sys
|
||||
sys.exit(1)
|
||||
|
||||
@staticmethod
|
||||
def compute_pitch(layer, preferred=True):
|
||||
|
||||
"""
|
||||
This is the preferred direction pitch
|
||||
i.e. we take the minimum or maximum contact dimension
|
||||
"""
|
||||
# Find the layer stacks this is used in
|
||||
from tech import layer_stacks
|
||||
pitches = []
|
||||
for stack in layer_stacks:
|
||||
# Compute the pitch with both vias above and below (if they exist)
|
||||
if stack[0] == layer:
|
||||
pitches.append(design.compute_layer_pitch(stack, preferred))
|
||||
if stack[2] == layer:
|
||||
pitches.append(design.compute_layer_pitch(stack[::-1], True))
|
||||
|
||||
return max(pitches)
|
||||
|
||||
@staticmethod
|
||||
def get_preferred_direction(layer):
|
||||
return preferred_directions[layer]
|
||||
|
||||
@staticmethod
|
||||
def compute_layer_pitch(layer_stack, preferred):
|
||||
|
||||
(layer1, via, layer2) = layer_stack
|
||||
try:
|
||||
if layer1 == "poly" or layer1 == "active":
|
||||
contact1 = getattr(contact, layer1 + "_contact")
|
||||
else:
|
||||
contact1 = getattr(contact, layer1 + "_via")
|
||||
except AttributeError:
|
||||
contact1 = getattr(contact, layer2 + "_via")
|
||||
|
||||
if preferred:
|
||||
if preferred_directions[layer1] == "V":
|
||||
contact_width = contact1.first_layer_width
|
||||
else:
|
||||
contact_width = contact1.first_layer_height
|
||||
else:
|
||||
if preferred_directions[layer1] == "V":
|
||||
contact_width = contact1.first_layer_height
|
||||
else:
|
||||
contact_width = contact1.first_layer_width
|
||||
layer_space = getattr(design, layer1 + "_space")
|
||||
|
||||
#print(layer_stack)
|
||||
#print(contact1)
|
||||
pitch = contact_width + layer_space
|
||||
|
||||
return utils.round_to_grid(pitch)
|
||||
|
||||
def setup_multiport_constants(self):
|
||||
"""
|
||||
These are contants and lists that aid multiport design.
|
||||
|
|
@ -316,6 +127,4 @@ class design(hierarchy_design):
|
|||
total_module_power += inst.mod.analytical_power(corner, load)
|
||||
return total_module_power
|
||||
|
||||
design.setup_drc_constants()
|
||||
design.setup_layer_constants()
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@
|
|||
This provides a set of useful generic types for the gdsMill interface.
|
||||
"""
|
||||
import debug
|
||||
from vector import vector
|
||||
from .vector import vector
|
||||
import tech
|
||||
import math
|
||||
import copy
|
||||
import numpy as np
|
||||
from globals import OPTS
|
||||
from utils import round_to_grid
|
||||
from .utils import round_to_grid
|
||||
|
||||
|
||||
class geometry:
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import hierarchy_layout
|
||||
import hierarchy_spice
|
||||
from .hierarchy_layout import layout
|
||||
from .hierarchy_spice import spice
|
||||
import debug
|
||||
import os
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||
class hierarchy_design(spice, layout):
|
||||
"""
|
||||
Design Class for all modules to inherit the base features.
|
||||
Class consisting of a set of modules and instances of these modules
|
||||
|
|
@ -22,8 +23,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
|||
self.drc_errors = "skipped"
|
||||
self.lvs_errors = "skipped"
|
||||
|
||||
hierarchy_spice.spice.__init__(self, name, cell_name)
|
||||
hierarchy_layout.layout.__init__(self, name, cell_name)
|
||||
# Flag for library cells which is recomputed in hierachy_layout
|
||||
gds_file = OPTS.openram_tech + "gds_lib/" + cell_name + ".gds"
|
||||
is_library_cell = os.path.isfile(gds_file)
|
||||
# Uniquify names to address the flat GDS namespace
|
||||
# except for the top/output name
|
||||
if not is_library_cell and name != OPTS.output_name and not name.startswith(OPTS.output_name):
|
||||
name = OPTS.output_name + "_" + name
|
||||
cell_name = name
|
||||
|
||||
spice.__init__(self, name, cell_name)
|
||||
layout.__init__(self, name, cell_name)
|
||||
self.init_graph_params()
|
||||
|
||||
def get_layout_pins(self, inst):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -12,10 +12,11 @@ import math
|
|||
import tech
|
||||
from globals import OPTS
|
||||
from pprint import pformat
|
||||
from delay_data import delay_data
|
||||
from wire_spice_model import wire_spice_model
|
||||
from power_data import power_data
|
||||
import logical_effort
|
||||
from .delay_data import delay_data
|
||||
from .wire_spice_model import wire_spice_model
|
||||
from .power_data import power_data
|
||||
from .logical_effort import convert_relative_c_to_farad, convert_farad_to_relative_c
|
||||
|
||||
|
||||
class spice():
|
||||
"""
|
||||
|
|
@ -36,19 +37,20 @@ class spice():
|
|||
# If we have a separate lvs directory, then all the lvs files
|
||||
# should be in there (all or nothing!)
|
||||
try:
|
||||
lvs_subdir = tech.lvs_lib
|
||||
except AttributeError:
|
||||
lvs_subdir = "lvs_lib"
|
||||
lvs_dir = OPTS.openram_tech + lvs_subdir + "/"
|
||||
from tech import lvs_name
|
||||
lvs_dir = OPTS.openram_tech + lvs_name + "_lvs_lib/"
|
||||
except ImportError:
|
||||
lvs_dir = OPTS.openram_tech + "lvs_lib/"
|
||||
if not os.path.exists(lvs_dir):
|
||||
lvs_dir = OPTS.openram_tech + "lvs_lib/"
|
||||
|
||||
if os.path.exists(lvs_dir):
|
||||
self.lvs_file = lvs_dir + cell_name + ".sp"
|
||||
else:
|
||||
self.lvs_file = lvs_dir + cell_name + ".sp"
|
||||
if not os.path.exists(self.lvs_file):
|
||||
self.lvs_file = self.sp_file
|
||||
|
||||
|
||||
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
|
||||
# Holds subckts/mods for this module
|
||||
self.mods = []
|
||||
self.mods = set()
|
||||
# Holds the pins for this module (in order)
|
||||
self.pins = []
|
||||
# The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND
|
||||
|
|
@ -128,7 +130,7 @@ class spice():
|
|||
|
||||
new_list = [input_list[x] for x in self.pin_indices]
|
||||
return new_list
|
||||
|
||||
|
||||
def add_pin_types(self, type_list):
|
||||
"""
|
||||
Add pin types for all the cell's pins.
|
||||
|
|
@ -140,7 +142,7 @@ class spice():
|
|||
\n Module names={}\
|
||||
".format(self.name, self.pins, type_list), 1)
|
||||
self.pin_type = {pin: type for pin, type in zip(self.pins, type_list)}
|
||||
|
||||
|
||||
def get_pin_type(self, name):
|
||||
""" Returns the type of the signal pin. """
|
||||
pin_type = self.pin_type[name]
|
||||
|
|
@ -187,10 +189,6 @@ class spice():
|
|||
inout_list.append(pin)
|
||||
return inout_list
|
||||
|
||||
def add_mod(self, mod):
|
||||
"""Adds a subckt/submodule to the subckt hierarchy"""
|
||||
self.mods.append(mod)
|
||||
|
||||
def connect_inst(self, args, check=True):
|
||||
"""
|
||||
Connects the pins of the last instance added
|
||||
|
|
@ -199,13 +197,13 @@ class spice():
|
|||
where we dynamically generate groups of connections after a
|
||||
group of modules are generated.
|
||||
"""
|
||||
|
||||
|
||||
num_pins = len(self.insts[-1].mod.pins)
|
||||
num_args = len(args)
|
||||
|
||||
# Order the arguments if the hard cell has a custom port order
|
||||
ordered_args = self.get_ordered_inputs(args)
|
||||
|
||||
|
||||
if (check and num_pins != num_args):
|
||||
if num_pins < num_args:
|
||||
mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins)
|
||||
|
|
@ -281,7 +279,10 @@ class spice():
|
|||
# parses line into ports and remove subckt
|
||||
lvs_pins = subckt_line.split(" ")[2:]
|
||||
debug.check(lvs_pins == self.pins,
|
||||
"Spice netlists for LVS and simulation have port mismatches: {0} (LVS) vs {1} (sim)".format(lvs_pins, self.pins))
|
||||
"Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins,
|
||||
self.lvs_file,
|
||||
self.pins,
|
||||
self.sp_file))
|
||||
|
||||
def check_net_in_spice(self, net_name):
|
||||
"""Checks if a net name exists in the current. Intended to be check nets in hand-made cells."""
|
||||
|
|
@ -372,15 +373,15 @@ class spice():
|
|||
# these are wires and paths
|
||||
if self.conns[i] == []:
|
||||
continue
|
||||
|
||||
|
||||
# Instance with no devices in it needs no subckt/instance
|
||||
if self.insts[i].mod.no_instances:
|
||||
continue
|
||||
|
||||
|
||||
# If this is a trimmed netlist, skip it by adding comment char
|
||||
if trim and self.insts[i].name in self.trim_insts:
|
||||
sp.write("* ")
|
||||
|
||||
|
||||
if lvs and hasattr(self.insts[i].mod, "lvs_device"):
|
||||
sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name,
|
||||
" ".join(self.conns[i])))
|
||||
|
|
@ -423,7 +424,7 @@ class spice():
|
|||
self.cacti_params = cacti_params
|
||||
# Get the r_on the the tx
|
||||
rd = self.get_on_resistance()
|
||||
# Calculate the intrinsic capacitance
|
||||
# Calculate the intrinsic capacitance
|
||||
c_intrinsic = self.get_intrinsic_capacitance()
|
||||
# Get wire values
|
||||
c_wire = self.module_wire_c()
|
||||
|
|
@ -442,7 +443,7 @@ class spice():
|
|||
|
||||
# FIXME: Slew is not used in the model right now.
|
||||
# Can be added heuristically as linear factor
|
||||
relative_cap = logical_effort.convert_farad_to_relative_c(load)
|
||||
relative_cap = convert_farad_to_relative_c(load)
|
||||
stage_effort = self.get_stage_effort(relative_cap)
|
||||
|
||||
# If it fails, then keep running with a valid object.
|
||||
|
|
@ -457,13 +458,13 @@ class spice():
|
|||
|
||||
def module_wire_c(self):
|
||||
"""All devices assumed to have ideal capacitance (0).
|
||||
Non-ideal cases should have this function re-defined.
|
||||
Non-ideal cases should have this function re-defined.
|
||||
"""
|
||||
return 0
|
||||
|
||||
def module_wire_r(self):
|
||||
"""All devices assumed to have ideal resistance (0).
|
||||
Non-ideal cases should have this function re-defined.
|
||||
Non-ideal cases should have this function re-defined.
|
||||
"""
|
||||
return 0
|
||||
|
||||
|
|
@ -510,7 +511,7 @@ class spice():
|
|||
# Override this function within a module if a more accurate input capacitance is needed.
|
||||
# Input/outputs with differing capacitances is not implemented.
|
||||
relative_cap = self.input_load()
|
||||
return logical_effort.convert_relative_c_to_farad(relative_cap)
|
||||
return convert_relative_c_to_farad(relative_cap)
|
||||
|
||||
def input_load(self):
|
||||
"""Inform users undefined relative capacitance functions used for analytical delays."""
|
||||
|
|
@ -521,19 +522,19 @@ class spice():
|
|||
self.cell_name))
|
||||
return 0
|
||||
|
||||
def cacti_rc_delay(self,
|
||||
def cacti_rc_delay(self,
|
||||
inputramptime, # input rise time
|
||||
tf, # time constant of gate
|
||||
vs1, # threshold voltage
|
||||
vs2, # threshold voltage
|
||||
rise, # whether input rises or fall
|
||||
extra_param_dict=None):
|
||||
extra_param_dict=None):
|
||||
"""By default, CACTI delay uses horowitz for gate delay.
|
||||
Can be overriden in cases like bitline if equation is different.
|
||||
"""
|
||||
return self.horowitz(inputramptime, tf, vs1, vs2, rise)
|
||||
|
||||
def horowitz(self,
|
||||
|
||||
def horowitz(self,
|
||||
inputramptime, # input rise time
|
||||
tf, # time constant of gate
|
||||
vs1, # threshold voltage
|
||||
|
|
@ -553,17 +554,17 @@ class spice():
|
|||
td = tf * math.sqrt(math.log(1.0 - vs1)*math.log(1.0 - vs1) + 2*a*b*(vs1)) + tf*(math.log(1.0 - vs1) - math.log(1.0 - vs2))
|
||||
|
||||
return td
|
||||
|
||||
def tr_r_on(self, width, is_nchannel, stack, _is_cell):
|
||||
|
||||
|
||||
def tr_r_on(self, width, is_nchannel, stack, _is_cell):
|
||||
|
||||
restrans = self.cacti_params["r_nch_on"] if is_nchannel else self.cacti_params["r_pch_on"]
|
||||
return stack * restrans / width
|
||||
|
||||
def gate_c(self, width):
|
||||
|
||||
|
||||
return (tech.spice["c_g_ideal"] + tech.spice["c_overlap"] + 3*tech.spice["c_fringe"])*width +\
|
||||
tech.drc["minlength_channel"]*tech.spice["cpolywire"]
|
||||
|
||||
|
||||
def drain_c_(self,
|
||||
width,
|
||||
stack,
|
||||
|
|
@ -574,10 +575,10 @@ class spice():
|
|||
c_fringe = 2*tech.spice["c_overlap"]
|
||||
c_overlap = 2*tech.spice["c_fringe"]
|
||||
drain_C_metal_connecting_folded_tr = 0
|
||||
|
||||
|
||||
w_folded_tr = width/folds
|
||||
num_folded_tr = folds
|
||||
|
||||
|
||||
# Re-created some logic contact to get minwidth as importing the contact
|
||||
# module causes a failure
|
||||
if "minwidth_contact" in tech.drc:
|
||||
|
|
@ -599,10 +600,10 @@ class spice():
|
|||
|
||||
if num_folded_tr%2 == 0:
|
||||
drain_h_for_sidewall = 0
|
||||
|
||||
|
||||
total_drain_height_for_cap_wrt_gate *= num_folded_tr
|
||||
drain_C_metal_connecting_folded_tr = tech.spice["wire_c_per_um"] * total_drain_w
|
||||
|
||||
|
||||
|
||||
drain_C_area = c_junc_area * total_drain_w * w_folded_tr
|
||||
drain_C_sidewall = c_junc_sidewall * (drain_h_for_sidewall + 2 * total_drain_w)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from base import vector
|
||||
from base import pin_layout
|
||||
from tech import layer_names
|
||||
import os
|
||||
import shutil
|
||||
from globals import OPTS
|
||||
from vector import vector
|
||||
from pin_layout import pin_layout
|
||||
|
||||
|
||||
class lef:
|
||||
|
|
@ -119,14 +119,13 @@ class lef:
|
|||
old_blockages = list(self.blockages[pin.layer])
|
||||
for blockage in old_blockages:
|
||||
if blockage.overlaps(inflated_pin):
|
||||
intersection_shape = blockage.intersection(inflated_pin)
|
||||
intersection_pin = blockage.intersection(inflated_pin)
|
||||
# If it is zero area, don't split the blockage
|
||||
if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
|
||||
if not intersection_pin or intersection_pin.area() == 0:
|
||||
continue
|
||||
|
||||
# Remove the old blockage and add the new ones
|
||||
self.blockages[pin.layer].remove(blockage)
|
||||
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
|
||||
new_blockages = blockage.cut(intersection_pin)
|
||||
self.blockages[pin.layer].extend(new_blockages)
|
||||
# We split something so make another pass
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from tech import drc, parameter, spice
|
||||
from tech import parameter
|
||||
|
||||
class logical_effort():
|
||||
"""
|
||||
|
|
@ -81,4 +81,4 @@ def convert_farad_to_relative_c(c_farad):
|
|||
|
||||
def convert_relative_c_to_farad(c_relative):
|
||||
"""Converts capacitance in logical effort relative units to Femto-Farads."""
|
||||
return c_relative/parameter['cap_relative_per_ff']
|
||||
return c_relative/parameter['cap_relative_per_ff']
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
import debug
|
||||
from tech import GDS, drc
|
||||
from vector import vector
|
||||
from .vector import vector
|
||||
from tech import layer, layer_indices
|
||||
import math
|
||||
|
||||
|
|
@ -174,6 +174,10 @@ class pin_layout:
|
|||
|
||||
def intersection(self, other):
|
||||
""" Check if a shape overlaps with a rectangle """
|
||||
|
||||
if not self.overlaps(other):
|
||||
return None
|
||||
|
||||
(ll, ur) = self.rect
|
||||
(oll, our) = other.rect
|
||||
|
||||
|
|
@ -182,7 +186,10 @@ class pin_layout:
|
|||
min_y = max(ll.y, oll.y)
|
||||
max_y = min(ur.y, our.y)
|
||||
|
||||
return [vector(min_x, min_y), vector(max_x, max_y)]
|
||||
if max_x - min_x == 0 or max_y - min_y == 0:
|
||||
return None
|
||||
|
||||
return pin_layout("", [vector(min_x, min_y), vector(max_x, max_y)], self.layer)
|
||||
|
||||
def xoverlaps(self, other):
|
||||
""" Check if shape has x overlap """
|
||||
|
|
@ -504,12 +511,19 @@ class pin_layout:
|
|||
elif other.contains(self):
|
||||
return math.inf
|
||||
else:
|
||||
intersections = self.compute_overlap_segment(other)
|
||||
intersections = set(self.compute_overlap_segment(other))
|
||||
# This is the common case where two pairs of edges overlap
|
||||
# at two points, so just find the distance between those two points
|
||||
if len(intersections) == 2:
|
||||
(p1, p2) = intersections
|
||||
return math.sqrt(pow(p1[0]-p2[0], 2) + pow(p1[1]-p2[1], 2))
|
||||
# If we have a rectangular overlap region
|
||||
elif len(intersections) == 4:
|
||||
points = intersections
|
||||
ll = vector(min(p.x for p in points), min(p.y for p in points))
|
||||
ur = vector(max(p.x for p in points), max(p.y for p in points))
|
||||
new_shape = pin_layout("", [ll, ur], self.lpp)
|
||||
return max(new_shape.height(), new_shape.width())
|
||||
else:
|
||||
# This is where we had a corner intersection or none
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from tech import drc
|
||||
import debug
|
||||
from design import design
|
||||
from .design import design
|
||||
from .vector import vector
|
||||
from .vector3d import vector3d
|
||||
from tech import drc
|
||||
from itertools import tee
|
||||
from vector import vector
|
||||
from vector3d import vector3d
|
||||
from sram_factory import factory
|
||||
|
||||
class route(design):
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@
|
|||
import os
|
||||
import math
|
||||
|
||||
import gdsMill
|
||||
from gdsMill import gdsMill
|
||||
import tech
|
||||
import globals
|
||||
import debug
|
||||
from vector import vector
|
||||
from pin_layout import pin_layout
|
||||
from .vector import vector
|
||||
from .pin_layout import pin_layout
|
||||
try:
|
||||
from tech import special_purposes
|
||||
except ImportError:
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ class vector():
|
|||
else:
|
||||
self.x=float(value[0])
|
||||
self.y=float(value[1])
|
||||
self._hash = hash((self.x,self.y))
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""
|
||||
|
|
@ -104,6 +105,7 @@ class vector():
|
|||
def snap_to_grid(self):
|
||||
self.x = self.snap_offset_to_grid(self.x)
|
||||
self.y = self.snap_offset_to_grid(self.y)
|
||||
self._hash = hash((self.x,self.y))
|
||||
return self
|
||||
|
||||
def snap_offset_to_grid(self, offset):
|
||||
|
|
|
|||
|
|
@ -94,12 +94,12 @@ class verilog:
|
|||
|
||||
self.vf.write("\n")
|
||||
|
||||
# This is the memory array itself
|
||||
self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n\n")
|
||||
|
||||
for port in self.all_ports:
|
||||
self.register_inputs(port)
|
||||
|
||||
# This is the memory array itself
|
||||
self.vf.write("reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n")
|
||||
|
||||
for port in self.all_ports:
|
||||
if port in self.write_ports:
|
||||
self.add_write_block(port)
|
||||
|
|
@ -162,7 +162,7 @@ class verilog:
|
|||
if port in self.read_ports:
|
||||
self.vf.write(" #(T_HOLD) dout{0} = {1}'bx;\n".format(port, self.word_size))
|
||||
if port in self.readwrite_ports:
|
||||
self.vf.write(" if ( !csb{0}_reg && web{0}_reg && VERBOSE ) \n".format(port))
|
||||
self.vf.write(" if ( !csb{0}_reg && web{0}_reg && VERBOSE )\n".format(port))
|
||||
self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port))
|
||||
elif port in self.read_ports:
|
||||
self.vf.write(" if ( !csb{0}_reg && VERBOSE ) \n".format(port))
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
from tech import drc
|
||||
import contact
|
||||
from wire_path import wire_path
|
||||
from .wire_path import wire_path
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
|
|
@ -69,15 +68,24 @@ class wire(wire_path):
|
|||
This is contact direction independent pitch,
|
||||
i.e. we take the maximum contact dimension
|
||||
"""
|
||||
|
||||
# This is here for the unit tests which may not have
|
||||
# initialized the static parts of the layout class yet.
|
||||
from base import layout
|
||||
layout("fake", "fake")
|
||||
|
||||
(layer1, via, layer2) = layer_stack
|
||||
|
||||
if layer1 == "poly" or layer1 == "active":
|
||||
contact1 = getattr(contact, layer1 + "_contact")
|
||||
try:
|
||||
contact1 = getattr(layout, layer1 + "_contact")
|
||||
except AttributeError:
|
||||
breakpoint()
|
||||
else:
|
||||
try:
|
||||
contact1 = getattr(contact, layer1 + "_via")
|
||||
contact1 = getattr(layout, layer1 + "_via")
|
||||
except AttributeError:
|
||||
contact1 = getattr(contact, layer2 + "_via")
|
||||
contact1 = getattr(layout, layer2 + "_via")
|
||||
max_contact = max(contact1.width, contact1.height)
|
||||
|
||||
layer1_space = drc("{0}_to_{0}".format(layer1))
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from .vector import vector
|
||||
from .utils import snap_to_grid
|
||||
from .design import design
|
||||
from tech import drc
|
||||
from tech import layer as techlayer
|
||||
import debug
|
||||
from vector import vector
|
||||
from utils import snap_to_grid
|
||||
|
||||
def create_rectilinear_route(my_list):
|
||||
""" Add intermediate nodes if it isn't rectilinear. Also skip
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import math
|
|||
import tech
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import timing_graph
|
||||
from base import timing_graph
|
||||
|
||||
|
||||
class simulation():
|
||||
|
|
@ -572,7 +572,7 @@ class simulation():
|
|||
self.sram.graph_exclude_column_mux(self.bitline_column, port)
|
||||
|
||||
# Generate new graph every analysis as edges might change depending on test bit
|
||||
self.graph = timing_graph.timing_graph()
|
||||
self.graph = timing_graph()
|
||||
self.sram_instance_name = "X{}".format(self.sram.name)
|
||||
self.sram.build_graph(self.graph, self.sram_instance_name, self.pins)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
from .datasheet_gen import datasheet_gen
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from table_gen import *
|
||||
from .table_gen import *
|
||||
import os
|
||||
import base64
|
||||
from globals import OPTS
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ from globals import OPTS
|
|||
import os
|
||||
import math
|
||||
import csv
|
||||
import datasheet
|
||||
import table_gen
|
||||
from .datasheet import datasheet
|
||||
from .table_gen import table_gen
|
||||
|
||||
# def process_name(corner):
|
||||
# """
|
||||
|
|
@ -400,7 +400,7 @@ def parse_characterizer_csv(f, pages):
|
|||
if found == 0:
|
||||
|
||||
# if this is the first corner for this sram, run first time configuration and set up tables
|
||||
new_sheet = datasheet.datasheet(NAME)
|
||||
new_sheet = datasheet(NAME)
|
||||
pages.append(new_sheet)
|
||||
|
||||
new_sheet.git_id = ORIGIN_ID
|
||||
|
|
@ -411,12 +411,12 @@ def parse_characterizer_csv(f, pages):
|
|||
new_sheet.description = [NAME, NUM_WORDS, NUM_BANKS, NUM_RW_PORTS, NUM_W_PORTS,
|
||||
NUM_R_PORTS, TECH_NAME, MIN_PERIOD, WORD_SIZE, ORIGIN_ID, DATETIME]
|
||||
|
||||
new_sheet.corners_table = table_gen.table_gen("corners")
|
||||
new_sheet.corners_table = table_gen("corners")
|
||||
new_sheet.corners_table.add_row(
|
||||
['Transistor Type', 'Power Supply', 'Temperature', 'Corner Name'])
|
||||
new_sheet.corners_table.add_row(
|
||||
[PROC, VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
|
||||
new_sheet.operating_table = table_gen.table_gen(
|
||||
new_sheet.operating_table = table_gen(
|
||||
"operating_table")
|
||||
new_sheet.operating_table.add_row(
|
||||
['Parameter', 'Min', 'Typ', 'Max', 'Units'])
|
||||
|
|
@ -432,10 +432,10 @@ def parse_characterizer_csv(f, pages):
|
|||
# failed to provide non-zero MIN_PERIOD
|
||||
new_sheet.operating_table.add_row(
|
||||
['Operating Frequency (F)', '', '', "not available in netlist only", 'MHz'])
|
||||
new_sheet.power_table = table_gen.table_gen("power")
|
||||
new_sheet.power_table = table_gen("power")
|
||||
new_sheet.power_table.add_row(
|
||||
['Pins', 'Mode', 'Power', 'Units'])
|
||||
new_sheet.timing_table = table_gen.table_gen("timing")
|
||||
new_sheet.timing_table = table_gen("timing")
|
||||
new_sheet.timing_table.add_row(
|
||||
['Parameter', 'Min', 'Max', 'Units'])
|
||||
# parse initial timing information
|
||||
|
|
@ -592,10 +592,10 @@ def parse_characterizer_csv(f, pages):
|
|||
else:
|
||||
break
|
||||
|
||||
new_sheet.dlv_table = table_gen.table_gen("dlv")
|
||||
new_sheet.dlv_table = table_gen("dlv")
|
||||
new_sheet.dlv_table.add_row(['Type', 'Description', 'Link'])
|
||||
|
||||
new_sheet.io_table = table_gen.table_gen("io")
|
||||
new_sheet.io_table = table_gen("io")
|
||||
new_sheet.io_table.add_row(['Type', 'Value'])
|
||||
|
||||
if not OPTS.netlist_only:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
|
||||
|
||||
class table_gen:
|
||||
"""small library of functions to generate the html tables"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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.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():
|
||||
"""
|
||||
|
|
@ -6,8 +6,8 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from drc_value import *
|
||||
from drc_lut import *
|
||||
from .drc_value import *
|
||||
from .drc_lut import *
|
||||
|
||||
|
||||
class design_rules(dict):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
VERSION = "1.1.19"
|
||||
VERSION = "1.2.0"
|
||||
NAME = "OpenRAM v{}".format(VERSION)
|
||||
USAGE = "openram.py [options] <config file>\nUse -h for help.\n"
|
||||
|
||||
|
|
@ -252,7 +252,8 @@ def setup_bitcell():
|
|||
|
||||
# See if bitcell exists
|
||||
try:
|
||||
__import__(OPTS.bitcell)
|
||||
c = importlib.import_module("modules." + OPTS.bitcell)
|
||||
mod = getattr(c, OPTS.bitcell)
|
||||
except ImportError:
|
||||
# Use the pbitcell if we couldn't find a custom bitcell
|
||||
# or its custom replica bitcell
|
||||
|
|
@ -377,7 +378,6 @@ def read_config(config_file, is_unit_test=True):
|
|||
ports,
|
||||
OPTS.tech_name)
|
||||
|
||||
|
||||
def end_openram():
|
||||
""" Clean up openram for a proper exit """
|
||||
cleanup_paths()
|
||||
|
|
@ -431,19 +431,12 @@ def setup_paths():
|
|||
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
||||
except:
|
||||
debug.error("$OPENRAM_HOME is not properly defined.", 1)
|
||||
|
||||
debug.check(os.path.isdir(OPENRAM_HOME),
|
||||
"$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME))
|
||||
|
||||
# Add all of the subdirs to the python path
|
||||
# These subdirs are modules and don't need
|
||||
# to be added: characterizer, verify
|
||||
subdirlist = [item for item in os.listdir(OPENRAM_HOME) if os.path.isdir(os.path.join(OPENRAM_HOME, item))]
|
||||
for subdir in subdirlist:
|
||||
full_path = "{0}/{1}".format(OPENRAM_HOME, subdir)
|
||||
debug.check(os.path.isdir(full_path),
|
||||
"$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path))
|
||||
if "__pycache__" not in full_path:
|
||||
sys.path.append("{0}".format(full_path))
|
||||
if OPENRAM_HOME not in sys.path:
|
||||
debug.error("Please add OPENRAM_HOME to the PYTHONPATH.", -1)
|
||||
|
||||
# Use a unique temp subdirectory if multithreaded
|
||||
if OPTS.num_threads > 1 or OPTS.openram_temp == "/tmp":
|
||||
|
|
@ -570,18 +563,18 @@ def import_tech():
|
|||
|
||||
OPTS.openram_tech = os.path.dirname(tech_mod.__file__) + "/"
|
||||
|
||||
# Add the tech directory
|
||||
# Prepend the tech directory so it is sourced FIRST
|
||||
tech_path = OPTS.openram_tech
|
||||
sys.path.append(tech_path)
|
||||
sys.path.insert(0, tech_path)
|
||||
try:
|
||||
import tech
|
||||
except ImportError:
|
||||
debug.error("Could not load tech module.", -1)
|
||||
|
||||
# Add custom modules of the technology to the path, if they exist
|
||||
# Prepend custom modules of the technology to the path, if they exist
|
||||
custom_mod_path = os.path.join(tech_path, "modules/")
|
||||
if os.path.exists(custom_mod_path):
|
||||
sys.path.append(custom_mod_path)
|
||||
sys.path.insert(0, custom_mod_path)
|
||||
|
||||
|
||||
def print_time(name, now_time, last_time=None, indentation=2):
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import design
|
||||
from base import vector
|
||||
from base import design
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import layer
|
||||
|
||||
|
||||
class and2_dec(design.design):
|
||||
class and2_dec(design):
|
||||
"""
|
||||
This is an AND with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||
|
||||
design.design.__init__(self, name)
|
||||
design.__init__(self, name)
|
||||
|
||||
debug.info(1, "Creating and2_dec {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
|
@ -43,9 +43,6 @@ class and2_dec(design.design):
|
|||
height=self.height,
|
||||
size=self.size)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
if "li" in layer:
|
||||
|
|
|
|||
|
|
@ -6,19 +6,19 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import design
|
||||
from base import design
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import layer
|
||||
|
||||
|
||||
class and3_dec(design.design):
|
||||
class and3_dec(design):
|
||||
"""
|
||||
This is an AND with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||
design.design.__init__(self, name)
|
||||
design.__init__(self, name)
|
||||
debug.info(1, "Creating and3_dec {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
self.size = size
|
||||
|
|
@ -41,9 +41,6 @@ class and3_dec(design.design):
|
|||
height=self.height,
|
||||
size=self.size)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
if "li" in layer:
|
||||
self.route_layer = "li"
|
||||
|
|
|
|||
|
|
@ -6,20 +6,20 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import design
|
||||
from base import design
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import layer
|
||||
|
||||
|
||||
class and4_dec(design.design):
|
||||
class and4_dec(design):
|
||||
"""
|
||||
This is an AND with configurable drive strength.
|
||||
"""
|
||||
def __init__(self, name, size=1, height=None, add_wells=True):
|
||||
|
||||
design.design.__init__(self, name)
|
||||
design.__init__(self, name)
|
||||
|
||||
debug.info(1, "Creating and4_dec {}".format(name))
|
||||
self.add_comment("size: {}".format(size))
|
||||
|
|
@ -43,9 +43,6 @@ class and4_dec(design.design):
|
|||
height=self.height,
|
||||
size=self.size)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
if "li" in layer:
|
||||
self.route_layer = "li"
|
||||
|
|
@ -129,4 +126,3 @@ class and4_dec(design.design):
|
|||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from math import log, ceil, floor
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from tech import layer_properties as layer_props
|
||||
|
||||
|
||||
class bank(design.design):
|
||||
class bank(design):
|
||||
"""
|
||||
Dynamically generated a single bank including bitcell array,
|
||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||
|
|
@ -229,7 +229,7 @@ class bank(design.design):
|
|||
|
||||
# UPPER LEFT QUADRANT
|
||||
# To the left of the bitcell array above the predecoders and control logic
|
||||
x_offset = self.m2_gap + self.port_address[port].width
|
||||
x_offset = self.decoder_gap + self.port_address[port].width
|
||||
self.port_address_offsets[port] = vector(-x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
||||
|
|
@ -272,7 +272,7 @@ class bank(design.design):
|
|||
|
||||
# LOWER RIGHT QUADRANT
|
||||
# To the right of the bitcell array
|
||||
x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap
|
||||
x_offset = self.bitcell_array_right + self.port_address[port].width + self.decoder_gap
|
||||
self.port_address_offsets[port] = vector(x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
|
||||
|
|
@ -364,9 +364,9 @@ class bank(design.design):
|
|||
self.num_col_addr_lines = 0
|
||||
self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines
|
||||
|
||||
# A space for wells or jogging m2
|
||||
self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3 * self.m2_pitch,
|
||||
# Gap between decoder and array
|
||||
self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
2 * self.m2_pitch,
|
||||
drc("nwell_to_nwell"))
|
||||
|
||||
def add_modules(self):
|
||||
|
|
@ -389,7 +389,6 @@ class bank(design.design):
|
|||
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
|
||||
cols=self.num_cols + self.num_spare_cols,
|
||||
rows=self.num_rows)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
self.port_address = []
|
||||
for port in self.all_ports:
|
||||
|
|
@ -397,21 +396,17 @@ class bank(design.design):
|
|||
cols=self.num_cols + self.num_spare_cols,
|
||||
rows=self.num_rows,
|
||||
port=port))
|
||||
self.add_mod(self.port_address[port])
|
||||
|
||||
self.port_data = []
|
||||
self.bit_offsets = self.get_column_offsets()
|
||||
for port in self.all_ports:
|
||||
temp_pre = factory.create(module_type="port_data",
|
||||
sram_config=self.sram_config,
|
||||
port=port,
|
||||
bit_offsets=self.bit_offsets)
|
||||
self.port_data.append(temp_pre)
|
||||
self.add_mod(self.port_data[port])
|
||||
self.port_data.append(factory.create(module_type="port_data",
|
||||
sram_config=self.sram_config,
|
||||
port=port,
|
||||
bit_offsets=self.bit_offsets))
|
||||
|
||||
if(self.num_banks > 1):
|
||||
self.bank_select = factory.create(module_type="bank_select")
|
||||
self.add_mod(self.bank_select)
|
||||
|
||||
def create_bitcell_array(self):
|
||||
""" Creating Bitcell Array """
|
||||
|
|
@ -528,26 +523,9 @@ class bank(design.design):
|
|||
|
||||
if self.col_addr_size == 0:
|
||||
return
|
||||
elif self.col_addr_size == 1:
|
||||
self.column_decoder = factory.create(module_type="pinvbuf",
|
||||
height=self.dff.height)
|
||||
elif self.col_addr_size == 2:
|
||||
self.column_decoder = factory.create(module_type="hierarchical_predecode2x4",
|
||||
column_decoder=True,
|
||||
height=self.dff.height)
|
||||
|
||||
elif self.col_addr_size == 3:
|
||||
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8",
|
||||
column_decoder=True,
|
||||
height=self.dff.height)
|
||||
elif self.col_addr_size == 4:
|
||||
self.column_decoder = factory.create(module_type="hierarchical_predecode4x16",
|
||||
column_decoder=True,
|
||||
height=self.dff.height)
|
||||
else:
|
||||
# No error checking before?
|
||||
debug.error("Invalid column decoder?", -1)
|
||||
self.add_mod(self.column_decoder)
|
||||
self.column_decoder = factory.create(module_type="column_decoder",
|
||||
col_addr_size=self.col_addr_size)
|
||||
|
||||
self.column_decoder_inst = [None] * len(self.all_ports)
|
||||
for port in self.all_ports:
|
||||
|
|
@ -611,15 +589,22 @@ class bank(design.design):
|
|||
|
||||
def route_supplies(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
|
||||
# Copy only the power pins already on the power layer
|
||||
# (this won't add vias to internal bitcell pins, for example)
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst, "vdd", add_vias=False)
|
||||
self.copy_power_pins(inst, "gnd", add_vias=False)
|
||||
|
||||
# This avoids getting copy errors on vias and other instances
|
||||
all_insts = [self.bitcell_array_inst] + self.port_address_inst + self.port_data_inst
|
||||
if hasattr(self, "column_decoder_inst"):
|
||||
all_insts += self.column_decoder_inst
|
||||
|
||||
for inst in all_insts:
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
|
||||
for pin_name, supply_name in zip(['vnb','vpb'],['gnd','vdd']):
|
||||
self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name)
|
||||
self.copy_layout_pin(self.bitcell_array_inst, pin_name, new_name=supply_name)
|
||||
|
||||
# If we use the pinvbuf as the decoder, we need to add power pins.
|
||||
# Other decoders already have them.
|
||||
|
|
@ -926,23 +911,14 @@ class bank(design.design):
|
|||
stack = getattr(self, layer_props.bank.stack)
|
||||
pitch = getattr(self, layer_props.bank.pitch)
|
||||
|
||||
if self.col_addr_size == 1:
|
||||
decode_names = []
|
||||
for i in range(self.num_col_addr_lines):
|
||||
decode_names.append("out_{}".format(i))
|
||||
|
||||
# Connect to sel[0] and sel[1]
|
||||
decode_names = ["Zb", "Z"]
|
||||
|
||||
# The Address LSB
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
decode_names = []
|
||||
for i in range(self.num_col_addr_lines):
|
||||
decode_names.append("out_{}".format(i))
|
||||
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in_{}".format(i)
|
||||
addr_name = "addr{0}_{1}".format(port, i)
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in_{}".format(i)
|
||||
addr_name = "addr{0}_{1}".format(port, i)
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||
|
||||
if port % 2:
|
||||
offset = self.column_decoder_inst[port].ll() - vector((self.num_col_addr_lines + 1) * pitch, 0)
|
||||
|
|
|
|||
|
|
@ -5,19 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
import design
|
||||
import contact
|
||||
from pinv import pinv
|
||||
from pnand2 import pnand2
|
||||
from pnor2 import pnor2
|
||||
from vector import vector
|
||||
from tech import drc
|
||||
from base import design
|
||||
from base import vector
|
||||
from pgates import pinv
|
||||
from pgates import pnand2
|
||||
from pgates import pnor2
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
class bank_select(design.design):
|
||||
class bank_select(design):
|
||||
"""Create a bank select signal that is combined with an array of
|
||||
NOR+INV gates to gate the control signals in case of multiple
|
||||
banks are created in upper level SRAM module
|
||||
|
|
@ -78,20 +75,15 @@ class bank_select(design.design):
|
|||
|
||||
# 1x Inverter
|
||||
self.inv_sel = factory.create(module_type="pinv", height=height)
|
||||
self.add_mod(self.inv_sel)
|
||||
|
||||
# 4x Inverter
|
||||
self.inv4x = factory.create(module_type="pinv", height=height, size=4)
|
||||
self.add_mod(self.inv4x)
|
||||
|
||||
self.nor2 = factory.create(module_type="pnor2", height=height)
|
||||
self.add_mod(self.nor2)
|
||||
|
||||
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
|
||||
self.add_mod(self.inv4x_nor)
|
||||
|
||||
self.nand2 = factory.create(module_type="pnand2", height=height)
|
||||
self.add_mod(self.nand2)
|
||||
|
||||
def calculate_module_offsets(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
#
|
||||
import debug
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class bitcell_1port(bitcell_base.bitcell_base):
|
||||
class bitcell_1port(bitcell_base):
|
||||
"""
|
||||
A single bit cell (6T, 8T, etc.) This module implements the
|
||||
single memory cell used in the design. It is a hand-made cell, so
|
||||
|
|
@ -7,10 +7,10 @@
|
|||
#
|
||||
import debug
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class bitcell_2port(bitcell_base.bitcell_base):
|
||||
class bitcell_2port(bitcell_base):
|
||||
"""
|
||||
A single bit cell (6T, 8T, etc.) This module implements the
|
||||
single memory cell used in the design. It is a hand-made cell, so
|
||||
|
|
@ -103,4 +103,4 @@ class bitcell_2port(bitcell_base.bitcell_base):
|
|||
def is_non_inverting(self):
|
||||
"""Return input to output polarity for module"""
|
||||
|
||||
return False
|
||||
return False
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from bitcell_base_array import bitcell_base_array
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
from tech import drc, spice
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
|
@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array):
|
|||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.route_supplies()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
|
@ -53,7 +55,6 @@ class bitcell_array(bitcell_base_array):
|
|||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
|
@ -64,11 +65,11 @@ class bitcell_array(bitcell_base_array):
|
|||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
|
||||
# If it is a "core" cell, it could be trimmed for sim time
|
||||
if col>0 and col<self.column_size-1 and row>0 and row<self.row_size-1:
|
||||
self.trim_insts.add(name)
|
||||
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
|
||||
|
|
|
|||
|
|
@ -7,18 +7,18 @@
|
|||
#
|
||||
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
from base import logical_effort
|
||||
from tech import parameter, drc, layer, spice
|
||||
|
||||
|
||||
class bitcell_base(design.design):
|
||||
class bitcell_base(design):
|
||||
"""
|
||||
Base bitcell parameters to be over-riden.
|
||||
"""
|
||||
def __init__(self, name, cell_name=None, prop=None):
|
||||
design.design.__init__(self, name, cell_name, prop)
|
||||
design.__init__(self, name, cell_name, prop)
|
||||
|
||||
# Set the bitcell specific properties
|
||||
if prop:
|
||||
|
|
@ -37,12 +37,12 @@ class bitcell_base(design.design):
|
|||
# min size NMOS gate load
|
||||
read_port_load = 0.5
|
||||
|
||||
return logical_effort.logical_effort('bitline',
|
||||
size,
|
||||
cin,
|
||||
load + read_port_load,
|
||||
parasitic_delay,
|
||||
False)
|
||||
return logical_effort('bitline',
|
||||
size,
|
||||
cin,
|
||||
load + read_port_load,
|
||||
parasitic_delay,
|
||||
False)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Bitcell power in nW. Only characterizes leakage."""
|
||||
|
|
@ -264,4 +264,4 @@ class bitcell_base(design.design):
|
|||
else:
|
||||
delay = math.sqrt(2*tstep*(vdd-spice["nom_threshold"])/m)
|
||||
|
||||
return delay
|
||||
return delay
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class bitcell_base_array(design.design):
|
||||
class bitcell_base_array(design):
|
||||
"""
|
||||
Abstract base class for bitcell-arrays -- bitcell, dummy, replica
|
||||
"""
|
||||
|
|
@ -43,11 +43,11 @@ class bitcell_base_array(design.design):
|
|||
# Make a flat list too
|
||||
self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl]
|
||||
|
||||
def create_all_wordline_names(self, row_size=None):
|
||||
def create_all_wordline_names(self, row_size=None, start_row=0):
|
||||
if row_size == None:
|
||||
row_size = self.row_size
|
||||
|
||||
for row in range(row_size):
|
||||
for row in range(start_row, row_size):
|
||||
for port in self.all_ports:
|
||||
self.wordline_names[port].append("wl_{0}_{1}".format(port, row))
|
||||
|
||||
|
|
@ -156,18 +156,15 @@ class bitcell_base_array(design.design):
|
|||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
def add_supply_pins(self):
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
def route_supplies(self):
|
||||
for inst in self.cell_inst.values():
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
self.add_bitline_pins()
|
||||
self.add_wl_pins()
|
||||
self.add_supply_pins()
|
||||
|
||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||
tempx = xoffset
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# Copyright (c) 2016-2021 Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
from bitcell_base_array import bitcell_base_array
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
|
@ -42,14 +42,13 @@ class col_cap_array(bitcell_base_array):
|
|||
|
||||
self.height = self.dummy_cell.height
|
||||
self.width = self.column_size * self.cell.width
|
||||
|
||||
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||
self.add_mod(self.dummy_cell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
|
@ -100,5 +99,4 @@ class col_cap_array(bitcell_base_array):
|
|||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for pin in inst.get_pins(pin_name):
|
||||
self.copy_power_pin(pin)
|
||||
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@
|
|||
#
|
||||
import debug
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class col_cap_bitcell_1port(bitcell_base.bitcell_base):
|
||||
class col_cap_bitcell_1port(bitcell_base):
|
||||
"""
|
||||
Column end cap cell.
|
||||
"""
|
||||
|
||||
def __init__(self, name="col_cap_bitcell_1port"):
|
||||
bitcell_base.bitcell_base.__init__(self, name, prop=props.col_cap_1port)
|
||||
bitcell_base.__init__(self, name, prop=props.col_cap_1port)
|
||||
debug.info(2, "Create col_cap bitcell 1 port object")
|
||||
|
||||
self.no_instances = True
|
||||
|
|
@ -7,16 +7,16 @@
|
|||
#
|
||||
import debug
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class col_cap_bitcell_2port(bitcell_base.bitcell_base):
|
||||
class col_cap_bitcell_2port(bitcell_base):
|
||||
"""
|
||||
Column end cap cell.
|
||||
"""
|
||||
|
||||
def __init__(self, name="col_cap_bitcell_2port"):
|
||||
bitcell_base.bitcell_base.__init__(self, name, prop=props.col_cap_2port)
|
||||
bitcell_base.__init__(self, name, prop=props.col_cap_2port)
|
||||
debug.info(2, "Create col_cap bitcell 2 port object")
|
||||
|
||||
self.no_instances = True
|
||||
|
|
@ -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)
|
||||
# All rights reserved.
|
||||
#
|
||||
import pgate
|
||||
from .pgate import *
|
||||
import debug
|
||||
from tech import drc, layer
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from .pgate import *
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties as cell_props
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class column_mux(pgate.pgate):
|
||||
class column_mux(pgate):
|
||||
"""
|
||||
This module implements the columnmux bitline cell used in the design.
|
||||
Creates a single column mux cell with the given integer size relative
|
||||
|
|
@ -77,7 +78,6 @@ class column_mux(pgate.pgate):
|
|||
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
||||
self.nmos = factory.create(module_type="ptx",
|
||||
width=self.ptx_width)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
# Space it in the center
|
||||
self.nmos_lower = self.add_inst(name="mux_tx1",
|
||||
|
|
@ -241,10 +241,9 @@ class column_mux(pgate.pgate):
|
|||
self.add_via_center(layers=self.col_mux_stack,
|
||||
offset=active_pos)
|
||||
|
||||
# Add the M1->..->power_grid_layer stack
|
||||
self.add_power_pin(name="gnd",
|
||||
loc=active_pos,
|
||||
start_layer="m1")
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="m1",
|
||||
offset=active_pos)
|
||||
|
||||
# Add well enclosure over all the tx and contact
|
||||
if "pwell" in layer:
|
||||
|
|
@ -5,16 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
from base import design
|
||||
import debug
|
||||
from tech import layer, preferred_directions
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import layer_properties as layer_props
|
||||
|
||||
|
||||
class column_mux_array(design.design):
|
||||
class column_mux_array(design):
|
||||
"""
|
||||
Dynamically generated column mux array.
|
||||
Array of column mux to read the bitlines through the 6T.
|
||||
|
|
@ -87,7 +87,6 @@ class column_mux_array(design.design):
|
|||
self.mux = factory.create(module_type="column_mux",
|
||||
bitcell_bl=self.bitcell_bl,
|
||||
bitcell_br=self.bitcell_br)
|
||||
self.add_mod(self.mux)
|
||||
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
|
|
@ -148,13 +147,14 @@ class column_mux_array(design.design):
|
|||
offset=offset,
|
||||
height=self.height - offset.y)
|
||||
|
||||
for inst in self.mux_inst:
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
def route_supplies(self):
|
||||
self.route_horizontal_pins("gnd", self.insts)
|
||||
|
||||
def add_routing(self):
|
||||
self.add_horizontal_input_rail()
|
||||
self.add_vertical_poly_rail()
|
||||
self.route_bitlines()
|
||||
self.route_supplies()
|
||||
|
||||
def add_horizontal_input_rail(self):
|
||||
""" Create address input rails below the mux transistors """
|
||||
|
|
@ -174,16 +174,17 @@ class column_mux_array(design.design):
|
|||
sel_index = col % self.words_per_row
|
||||
# Add the column x offset to find the right select bit
|
||||
gate_offset = self.mux_inst[col].get_pin("sel").bc()
|
||||
# height to connect the gate to the correct horizontal row
|
||||
# sel_height = self.get_pin("sel_{}".format(sel_index)).by()
|
||||
# use the y offset from the sel pin and the x offset from the gate
|
||||
|
||||
offset = vector(gate_offset.x,
|
||||
self.get_pin("sel_{}".format(sel_index)).cy())
|
||||
|
||||
bl_offset = offset.scale(0, 1) + vector((self.mux_inst[col].get_pin("br_out").cx() + self.mux_inst[col].get_pin("bl_out").cx())/2, 0)
|
||||
self.add_via_stack_center(from_layer="poly",
|
||||
to_layer=self.sel_layer,
|
||||
offset=offset,
|
||||
offset=bl_offset,
|
||||
directions=self.via_directions)
|
||||
self.add_path("poly", [offset, gate_offset])
|
||||
self.add_path("poly", [offset, gate_offset, bl_offset])
|
||||
|
||||
def route_bitlines(self):
|
||||
""" Connect the output bit-lines to form the appropriate width mux """
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
from base import design
|
||||
import debug
|
||||
from sram_factory import factory
|
||||
import math
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
from base import logical_effort
|
||||
|
||||
|
||||
class control_logic(design.design):
|
||||
class control_logic(design):
|
||||
"""
|
||||
Dynamically generated Control logic for the total SRAM circuit.
|
||||
"""
|
||||
|
|
@ -43,7 +43,7 @@ class control_logic(design.design):
|
|||
self.num_words = num_rows * words_per_row
|
||||
|
||||
self.enable_delay_chain_resizing = False
|
||||
self.inv_parasitic_delay = logical_effort.logical_effort.pinv
|
||||
self.inv_parasitic_delay = logical_effort.pinv
|
||||
|
||||
# Determines how much larger the sen delay should be. Accounts for possible error in model.
|
||||
# FIXME: This should be made a parameter
|
||||
|
|
@ -91,17 +91,13 @@ class control_logic(design.design):
|
|||
rows=self.num_control_signals,
|
||||
columns=1)
|
||||
|
||||
self.add_mod(self.ctrl_dff_array)
|
||||
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
size=12,
|
||||
height=dff_height)
|
||||
self.add_mod(self.and2)
|
||||
|
||||
self.rbl_driver = factory.create(module_type="pbuf",
|
||||
size=self.num_cols,
|
||||
height=dff_height)
|
||||
self.add_mod(self.rbl_driver)
|
||||
|
||||
# clk_buf drives a flop for every address
|
||||
addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
|
||||
|
|
@ -114,8 +110,6 @@ class control_logic(design.design):
|
|||
fanout=clock_fanout,
|
||||
height=dff_height)
|
||||
|
||||
self.add_mod(self.clk_buf_driver)
|
||||
|
||||
# We will use the maximum since this same value is used to size the wl_en
|
||||
# and the p_en_bar drivers
|
||||
# max_fanout = max(self.num_rows, self.num_cols)
|
||||
|
|
@ -126,25 +120,21 @@ class control_logic(design.design):
|
|||
self.wl_en_driver = factory.create(module_type="pdriver",
|
||||
size_list=size_list,
|
||||
height=dff_height)
|
||||
self.add_mod(self.wl_en_driver)
|
||||
|
||||
# w_en drives every write driver
|
||||
self.wen_and = factory.create(module_type="pand3",
|
||||
size=self.word_size + 8,
|
||||
height=dff_height)
|
||||
self.add_mod(self.wen_and)
|
||||
size=self.word_size + 8,
|
||||
height=dff_height)
|
||||
|
||||
# s_en drives every sense amp
|
||||
self.sen_and3 = factory.create(module_type="pand3",
|
||||
size=self.word_size + self.num_spare_cols,
|
||||
height=dff_height)
|
||||
self.add_mod(self.sen_and3)
|
||||
|
||||
# used to generate inverted signals with low fanout
|
||||
self.inv = factory.create(module_type="pinv",
|
||||
size=1,
|
||||
height=dff_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
# p_en_bar drives every column in the bitcell array
|
||||
# but it is sized the same as the wl_en driver with
|
||||
|
|
@ -152,17 +142,14 @@ class control_logic(design.design):
|
|||
self.p_en_bar_driver = factory.create(module_type="pdriver",
|
||||
fanout=self.num_cols,
|
||||
height=dff_height)
|
||||
self.add_mod(self.p_en_bar_driver)
|
||||
|
||||
self.nand2 = factory.create(module_type="pnand2",
|
||||
height=dff_height)
|
||||
self.add_mod(self.nand2)
|
||||
|
||||
debug.check(OPTS.delay_chain_stages % 2,
|
||||
"Must use odd number of delay chain stages for inverting delay chain.")
|
||||
self.delay_chain=factory.create(module_type="delay_chain",
|
||||
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
|
||||
self.add_mod(self.delay_chain)
|
||||
|
||||
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
|
||||
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
|
||||
|
|
@ -293,7 +280,8 @@ class control_logic(design.design):
|
|||
def route_rails(self):
|
||||
""" Add the input signal inverted tracks """
|
||||
height = self.control_logic_center.y - self.m2_pitch
|
||||
offset = vector(self.ctrl_dff_array.width, 0)
|
||||
# DFF spacing plus the power routing
|
||||
offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0)
|
||||
|
||||
self.input_bus = self.create_vertical_bus("m2",
|
||||
offset,
|
||||
|
|
@ -325,7 +313,8 @@ class control_logic(design.design):
|
|||
self.place_dffs()
|
||||
|
||||
# All of the control logic is placed to the right of the DFFs and bus
|
||||
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
|
||||
# as well as the power supply stripe
|
||||
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch
|
||||
|
||||
row = 0
|
||||
# Add the logic on the right of the bus
|
||||
|
|
@ -381,7 +370,7 @@ class control_logic(design.design):
|
|||
self.route_clk_buf()
|
||||
self.route_gated_clk_bar()
|
||||
self.route_gated_clk_buf()
|
||||
self.route_supply()
|
||||
self.route_supplies()
|
||||
|
||||
def create_delay(self):
|
||||
""" Create the replica bitline """
|
||||
|
|
@ -720,28 +709,74 @@ class control_logic(design.design):
|
|||
start=out_pos,
|
||||
end=right_pos)
|
||||
|
||||
def route_supply(self):
|
||||
def route_supplies(self):
|
||||
""" Add vdd and gnd to the instance cells """
|
||||
|
||||
supply_layer = self.dff.get_pin("vdd").layer
|
||||
pin_layer = self.dff.get_pin("vdd").layer
|
||||
supply_layer = self.supply_stack[2]
|
||||
|
||||
|
||||
# FIXME: We should be able to replace this with route_vertical_pins instead
|
||||
# but we may have to make the logic gates a separate module so that they
|
||||
# have row pins of the same width
|
||||
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
|
||||
min_row_x_loc = self.control_x_offset
|
||||
|
||||
vdd_pin_locs = []
|
||||
gnd_pin_locs = []
|
||||
|
||||
last_via = None
|
||||
for inst in self.row_end_inst:
|
||||
pins = inst.get_pins("vdd")
|
||||
for pin in pins:
|
||||
if pin.layer == supply_layer:
|
||||
if pin.layer == pin_layer:
|
||||
row_loc = pin.rc()
|
||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
||||
self.add_power_pin("vdd", pin_loc, start_layer=pin.layer)
|
||||
self.add_path(supply_layer, [row_loc, pin_loc])
|
||||
vdd_pin_locs.append(pin_loc)
|
||||
last_via = self.add_via_stack_center(from_layer=pin_layer,
|
||||
to_layer=supply_layer,
|
||||
offset=pin_loc,
|
||||
min_area=True)
|
||||
self.add_path(pin_layer, [row_loc, pin_loc])
|
||||
|
||||
pins = inst.get_pins("gnd")
|
||||
for pin in pins:
|
||||
if pin.layer == supply_layer:
|
||||
if pin.layer == pin_layer:
|
||||
row_loc = pin.rc()
|
||||
pin_loc = vector(max_row_x_loc, pin.rc().y)
|
||||
self.add_power_pin("gnd", pin_loc, start_layer=pin.layer)
|
||||
self.add_path(supply_layer, [row_loc, pin_loc])
|
||||
pin_loc = vector(min_row_x_loc, pin.rc().y)
|
||||
gnd_pin_locs.append(pin_loc)
|
||||
last_via = self.add_via_stack_center(from_layer=pin_layer,
|
||||
to_layer=supply_layer,
|
||||
offset=pin_loc,
|
||||
min_area=True)
|
||||
self.add_path(pin_layer, [row_loc, pin_loc])
|
||||
|
||||
if last_via:
|
||||
via_height=last_via.mod.second_layer_height
|
||||
via_width=last_via.mod.second_layer_width
|
||||
else:
|
||||
via_height=None
|
||||
via_width=0
|
||||
|
||||
min_y = min([x.y for x in vdd_pin_locs])
|
||||
max_y = max([x.y for x in vdd_pin_locs])
|
||||
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
|
||||
top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height)
|
||||
self.add_layout_pin_segment_center(text="vdd",
|
||||
layer=supply_layer,
|
||||
start=bot_pos,
|
||||
end=top_pos,
|
||||
width=via_width)
|
||||
|
||||
min_y = min([x.y for x in gnd_pin_locs])
|
||||
max_y = max([x.y for x in gnd_pin_locs])
|
||||
bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height)
|
||||
top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height)
|
||||
self.add_layout_pin_segment_center(text="gnd",
|
||||
layer=supply_layer,
|
||||
start=bot_pos,
|
||||
end=top_pos,
|
||||
width=via_width)
|
||||
|
||||
self.copy_layout_pin(self.delay_inst, "gnd")
|
||||
self.copy_layout_pin(self.delay_inst, "vdd")
|
||||
|
|
@ -794,7 +829,7 @@ class control_logic(design.design):
|
|||
# Connect this at the bottom of the buffer
|
||||
out_pin = inst.get_pin("Z")
|
||||
out_pos = out_pin.center()
|
||||
mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height)
|
||||
mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height)
|
||||
mid2 = vector(self.input_bus[name].cx(), mid1.y)
|
||||
bus_pos = self.input_bus[name].center()
|
||||
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from base import design
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class delay_chain(design.design):
|
||||
class delay_chain(design):
|
||||
"""
|
||||
Generate a delay chain with the given number of stages and fanout.
|
||||
Input is a list contains the electrical effort (fanout) of each stage.
|
||||
|
|
@ -47,7 +47,7 @@ class delay_chain(design.design):
|
|||
self.height = self.rows * self.inv.height
|
||||
# The width is determined by the largest fanout plus the driver
|
||||
self.width = (max(self.fanout_list) + 1) * self.inv.width
|
||||
|
||||
|
||||
self.place_inverters()
|
||||
self.route_inverters()
|
||||
self.route_supplies()
|
||||
|
|
@ -66,10 +66,9 @@ class delay_chain(design.design):
|
|||
|
||||
self.dff = factory.create(module_type="dff_buf")
|
||||
dff_height = self.dff.height
|
||||
|
||||
|
||||
self.inv = factory.create(module_type="pinv",
|
||||
height=dff_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_inverters(self):
|
||||
""" Create the inverters and connect them based on the stage list """
|
||||
|
|
@ -173,20 +172,11 @@ class delay_chain(design.design):
|
|||
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
|
||||
|
||||
def route_supplies(self):
|
||||
# Add power and ground to all the cells except:
|
||||
# the fanout driver, the right-most load
|
||||
# The routing to connect the loads is over the first and last cells
|
||||
# We have an even number of drivers and must only do every other
|
||||
# supply rail
|
||||
|
||||
for inst in self.driver_inst_list:
|
||||
load_list = self.load_inst_map[inst]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
pin = load_list[0].get_pin(pin_name)
|
||||
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
|
||||
|
||||
pin = load_list[-2].get_pin(pin_name)
|
||||
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
|
||||
# These pins get routed in one cell from the left/right
|
||||
# because the input signal gets routed on M3 and can interfere with the delay input.
|
||||
self.route_vertical_pins("vdd", self.driver_inst_list, xside="rx")
|
||||
right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list]
|
||||
self.route_vertical_pins("gnd", right_load_insts, xside="lx")
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
|
|
@ -199,7 +189,7 @@ class delay_chain(design.design):
|
|||
to_layer="m2",
|
||||
offset=mid_loc)
|
||||
self.add_path(a_pin.layer, [a_pin.center(), mid_loc])
|
||||
|
||||
|
||||
self.add_layout_pin_rect_center(text="in",
|
||||
layer="m2",
|
||||
offset=mid_loc)
|
||||
|
|
@ -213,4 +203,3 @@ class delay_chain(design.design):
|
|||
self.add_layout_pin_rect_center(text="out",
|
||||
layer="m1",
|
||||
offset=a_pin.center())
|
||||
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
from base import design
|
||||
from tech import cell_properties as props
|
||||
from tech import spice
|
||||
|
||||
|
||||
class dff(design.design):
|
||||
class dff(design):
|
||||
"""
|
||||
Memory address flip-flop
|
||||
"""
|
||||
|
|
@ -6,13 +6,13 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from base import design
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class dff_array(design.design):
|
||||
class dff_array(design):
|
||||
"""
|
||||
This is a simple row (or multiple rows) of flops.
|
||||
Unlike the data flops, these are never spaced out.
|
||||
|
|
@ -42,13 +42,13 @@ class dff_array(design.design):
|
|||
self.height = self.rows * self.dff.height
|
||||
|
||||
self.place_dff_array()
|
||||
self.route_supplies()
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
self.dff = factory.create(module_type="dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
def add_pins(self):
|
||||
for row in range(self.rows):
|
||||
|
|
@ -107,17 +107,26 @@ class dff_array(design.design):
|
|||
|
||||
return dout_name
|
||||
|
||||
def route_supplies(self):
|
||||
if self.rows > 1:
|
||||
# Vertical straps on ends if multiple rows
|
||||
left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)]
|
||||
right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)]
|
||||
self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy")
|
||||
self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy")
|
||||
else:
|
||||
|
||||
# Add connections every 4 cells
|
||||
for col in range(0, self.columns, 4):
|
||||
vdd_pin=self.dff_insts[0, col].get_pin("vdd")
|
||||
self.add_power_pin("vdd", vdd_pin.lc(), start_layer=vdd_pin.layer)
|
||||
|
||||
# Add connections every 4 cells
|
||||
for col in range(0, self.columns, 4):
|
||||
gnd_pin=self.dff_insts[0, col].get_pin("gnd")
|
||||
self.add_power_pin("gnd", gnd_pin.rc(), start_layer=gnd_pin.layer)
|
||||
|
||||
def add_layout_pins(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row, col].get_pin("vdd")
|
||||
self.copy_power_pin(vdd_pin)
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
||||
self.copy_power_pin(gnd_pin)
|
||||
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
din_pin = self.dff_insts[row, col].get_pin("D")
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
from tech import layer
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class dff_buf(design.design):
|
||||
class dff_buf(design):
|
||||
"""
|
||||
This is a simple buffered DFF. The output is buffered
|
||||
with two inverters, of variable size, to provide q
|
||||
|
|
@ -58,17 +58,14 @@ class dff_buf(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
self.dff = factory.create(module_type="dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.inv1 = factory.create(module_type="pinv",
|
||||
size=self.inv1_size,
|
||||
height=self.dff.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = factory.create(module_type="pinv",
|
||||
size=self.inv2_size,
|
||||
height=self.dff.height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin_list(["D", "Q", "Qb", "clk", "vdd", "gnd"],
|
||||
|
|
@ -110,7 +107,7 @@ class dff_buf(design.design):
|
|||
pass
|
||||
|
||||
well_spacing += 2 * self.well_extend_active
|
||||
|
||||
|
||||
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing, 0))
|
||||
|
||||
# Add INV2 to the right
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from base import design
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class dff_buf_array(design.design):
|
||||
class dff_buf_array(design):
|
||||
"""
|
||||
This is a simple row (or multiple rows) of flops.
|
||||
Unlike the data flops, these are never spaced out.
|
||||
|
|
@ -68,7 +68,6 @@ class dff_buf_array(design.design):
|
|||
self.dff = factory.create(module_type="dff_buf",
|
||||
inv1_size=self.inv1_size,
|
||||
inv2_size=self.inv2_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
|
|
@ -155,15 +154,23 @@ class dff_buf_array(design.design):
|
|||
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
|
||||
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
|
||||
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row, col].get_pin("vdd")
|
||||
self.copy_power_pin(vdd_pin, loc=vdd_pin.lc())
|
||||
if self.rows > 1:
|
||||
# Vertical straps on ends if multiple rows
|
||||
left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)]
|
||||
right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)]
|
||||
self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy")
|
||||
self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy")
|
||||
else:
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row, col].get_pin("vdd")
|
||||
self.copy_power_pin(vdd_pin)
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
||||
self.copy_power_pin(gnd_pin)
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
||||
self.copy_power_pin(gnd_pin, loc=gnd_pin.lc())
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,13 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from tech import drc
|
||||
from math import log
|
||||
from vector import vector
|
||||
from base import design
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from pinv import pinv
|
||||
from sram_factory import factory
|
||||
|
||||
class dff_inv(design.design):
|
||||
|
||||
class dff_inv(design):
|
||||
"""
|
||||
This is a simple DFF with an inverted output. Some DFFs
|
||||
do not have Qbar, so this will create it.
|
||||
|
|
@ -66,12 +65,10 @@ class dff_inv(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
self.dff = dff_inv.dff_inv(self.inv_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
self.inv1 = factory.create(module_type="pinv",
|
||||
size=self.inv_size,
|
||||
height=self.dff.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
def create_modules(self):
|
||||
self.dff_inst=self.add_inst(name="dff_inv_dff",
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from base import design
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class dff_inv_array(design.design):
|
||||
class dff_inv_array(design):
|
||||
"""
|
||||
This is a simple row (or multiple rows) of flops.
|
||||
Unlike the data flops, these are never spaced out.
|
||||
|
|
@ -53,7 +53,6 @@ class dff_inv_array(design.design):
|
|||
|
||||
def add_modules(self):
|
||||
self.dff = factory.create(module_type="dff")
|
||||
self.add_mod(self.dff)
|
||||
|
||||
def add_pins(self):
|
||||
for row in range(self.rows):
|
||||
|
|
@ -129,11 +128,11 @@ class dff_inv_array(design.design):
|
|||
for col in range(self.columns):
|
||||
# Adds power pin on left of row
|
||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
||||
self.add_power_pin(vdd_pin, loc=vdd_pin.lc())
|
||||
self.add_power_pin(vdd_pin, loc=vdd_pin.lc(), start_layer=vdd_pin.layer)
|
||||
|
||||
# Adds gnd pin on left of row
|
||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
||||
self.add_power_pin(gnd_pin, loc=gnd_pin.lc())
|
||||
self.add_power_pin(gnd_pin, loc=gnd_pin.lc(), start_layer=gnd_pin.layer)
|
||||
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# Copyright (c) 2016-2021 Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
from bitcell_base_array import bitcell_base_array
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
|
|
@ -36,6 +36,8 @@ class dummy_array(bitcell_base_array):
|
|||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.route_supplies()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
|
@ -45,7 +47,6 @@ class dummy_array(bitcell_base_array):
|
|||
|
||||
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
self.add_mod(self.dummy_cell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
|
@ -99,6 +100,8 @@ class dummy_array(bitcell_base_array):
|
|||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
def route_supplies(self):
|
||||
|
||||
# Copy a vdd/gnd layout pin from every cell
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
#
|
||||
import debug
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class dummy_bitcell_1port(bitcell_base.bitcell_base):
|
||||
class dummy_bitcell_1port(bitcell_base):
|
||||
"""
|
||||
A single bit cell (6T, 8T, etc.) This module implements the
|
||||
single memory cell used in the design. It is a hand-made cell, so
|
||||
|
|
@ -7,10 +7,10 @@
|
|||
#
|
||||
import debug
|
||||
from tech import cell_properties as props
|
||||
import bitcell_base
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class dummy_bitcell_2port(bitcell_base.bitcell_base):
|
||||
class dummy_bitcell_2port(bitcell_base):
|
||||
"""
|
||||
A single bit cell which is forced to store a 0.
|
||||
This module implements the single memory cell used in the design. It
|
||||
|
|
@ -6,13 +6,13 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from base import design
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class dummy_pbitcell(design.design):
|
||||
class dummy_pbitcell(design):
|
||||
"""
|
||||
Creates a replica bitcell using pbitcell
|
||||
"""
|
||||
|
|
@ -23,7 +23,7 @@ class dummy_pbitcell(design.design):
|
|||
self.num_r_ports = OPTS.num_r_ports
|
||||
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
|
||||
|
||||
design.design.__init__(self, name)
|
||||
design.__init__(self, name)
|
||||
debug.info(1, "create a dummy bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
|
||||
self.num_w_ports,
|
||||
self.num_r_ports))
|
||||
|
|
@ -56,7 +56,6 @@ class dummy_pbitcell(design.design):
|
|||
def add_modules(self):
|
||||
self.prbc = factory.create(module_type="pbitcell",
|
||||
dummy_bitcell=True)
|
||||
self.add_mod(self.prbc)
|
||||
|
||||
self.height = self.prbc.height
|
||||
self.width = self.prbc.width
|
||||
|
|
@ -5,16 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import bitcell_base_array
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from base import vector
|
||||
import debug
|
||||
from numpy import cumsum
|
||||
from tech import layer_properties as layer_props
|
||||
|
||||
|
||||
class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||
class global_bitcell_array(bitcell_base_array):
|
||||
"""
|
||||
Creates a global bitcell array.
|
||||
Rows is an integer number for all local arrays.
|
||||
|
|
@ -64,7 +64,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
|||
rbl=self.rbl,
|
||||
left_rbl=[0],
|
||||
right_rbl=[1] if len(self.all_ports) > 1 else [])
|
||||
self.add_mod(la)
|
||||
self.local_mods.append(la)
|
||||
return
|
||||
|
||||
|
|
@ -90,7 +89,6 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
|||
cols=cols,
|
||||
rbl=self.rbl)
|
||||
|
||||
self.add_mod(la)
|
||||
self.local_mods.append(la)
|
||||
|
||||
def add_pins(self):
|
||||
|
|
@ -344,4 +342,3 @@ class global_bitcell_array(bitcell_base_array.bitcell_base_array):
|
|||
"""Exclude dffs from graph as they do not represent critical path"""
|
||||
|
||||
self.graph_inst_exclude.add(self.ctrl_dff_inst)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
import math
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from tech import layer_indices
|
||||
from tech import layer_stacks
|
||||
from tech import layer_properties as layer_props
|
||||
from tech import drc
|
||||
|
||||
class hierarchical_decoder(design.design):
|
||||
class hierarchical_decoder(design):
|
||||
"""
|
||||
Dynamically generated hierarchical decoder.
|
||||
"""
|
||||
|
|
@ -57,11 +57,12 @@ class hierarchical_decoder(design.design):
|
|||
self.route_inputs()
|
||||
self.route_outputs()
|
||||
self.route_decoder_bus()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
self.offset_x_coordinates()
|
||||
|
||||
self.width = self.and_inst[0].rx() + 0.5 * self.m1_width
|
||||
self.width = self.and_inst[0].rx()
|
||||
|
||||
self.route_supplies()
|
||||
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
|
@ -69,14 +70,11 @@ class hierarchical_decoder(design.design):
|
|||
def add_modules(self):
|
||||
self.and2 = factory.create(module_type="and2_dec",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and2)
|
||||
|
||||
self.and3 = factory.create(module_type="and3_dec",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and3)
|
||||
# TBD
|
||||
# self.and4 = factory.create(module_type="and4_dec")
|
||||
# self.add_mod(self.and4)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
|
|
@ -84,15 +82,12 @@ class hierarchical_decoder(design.design):
|
|||
""" Create the decoders based on the number of pre-decodes """
|
||||
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.pre2_4)
|
||||
|
||||
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.pre3_8)
|
||||
|
||||
self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.pre4_16)
|
||||
|
||||
def determine_predecodes(self, num_inputs):
|
||||
"""
|
||||
|
|
@ -194,7 +189,7 @@ class hierarchical_decoder(design.design):
|
|||
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")
|
||||
|
||||
# Two extra pitches between modules on left and right
|
||||
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
|
||||
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + 2 * self.bus_pitch
|
||||
self.row_decoder_height = self.and2.height * self.num_outputs
|
||||
|
||||
# Extra bus space for supply contacts
|
||||
|
|
@ -501,11 +496,11 @@ class hierarchical_decoder(design.design):
|
|||
if (self.num_inputs >= 4):
|
||||
# This leaves an offset for the predecoder output jogs
|
||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||
pitch=self.bus_pitch,
|
||||
offset=vector(self.bus_pitch, 0),
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
self.predecode_bus = self.create_vertical_bus(layer=self.bus_layer,
|
||||
pitch=self.bus_pitch,
|
||||
offset=vector(self.bus_pitch, 0),
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
self.route_bus_to_decoder()
|
||||
self.route_predecodes_to_bus()
|
||||
|
||||
|
|
@ -593,44 +588,20 @@ class hierarchical_decoder(design.design):
|
|||
output_index)
|
||||
output_index = output_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
def route_supplies(self):
|
||||
"""
|
||||
Add a pin for each row of vdd/gnd which are
|
||||
must-connects next level up.
|
||||
"""
|
||||
|
||||
if layer_props.hierarchical_decoder.vertical_supply:
|
||||
for n in ["vdd", "gnd"]:
|
||||
pins = self.and_inst[0].get_pins(n)
|
||||
for pin in pins:
|
||||
self.add_rect(layer=pin.layer,
|
||||
offset=pin.ll() + vector(0, self.bus_space),
|
||||
width=pin.width(),
|
||||
height=self.height - 2 * self.bus_space)
|
||||
# Leave these to route in the port_address
|
||||
all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst
|
||||
for inst in all_insts:
|
||||
self.copy_layout_pin(inst, "vdd")
|
||||
self.copy_layout_pin(inst, "gnd")
|
||||
|
||||
self.route_vertical_pins("vdd", self.and_inst, xside="rx",)
|
||||
self.route_vertical_pins("gnd", self.and_inst, xside="lx",)
|
||||
|
||||
# This adds power vias at the top of each cell
|
||||
# (except the last to keep them inside the boundary)
|
||||
for i in self.and_inst[:-1]:
|
||||
pins = i.get_pins(n)
|
||||
for pin in pins:
|
||||
self.copy_power_pin(pin, loc=pin.uc())
|
||||
|
||||
for i in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(i, n)
|
||||
else:
|
||||
# The vias will be placed at the right of the cells.
|
||||
xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space
|
||||
for row in range(0, self.num_outputs):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.and_inst[row].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.copy_power_pin(supply_pin, loc=pin_pos)
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst:
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(pre, pin_name)
|
||||
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
||||
"""
|
||||
|
|
@ -698,7 +669,7 @@ class hierarchical_decoder(design.design):
|
|||
drc_error = 0
|
||||
for and_input in self.predecode_bus_rail_pos:
|
||||
if and_input.x == rail_pos.x:
|
||||
if (abs(y_offset - and_input.y) < total_buffer_space) or (abs(y_offset - and_input.y) < via.height):
|
||||
if (abs(y_offset - and_input.y) < total_buffer_space) or (abs(y_offset - and_input.y) < via.height) or (abs(y_offset - drc("minwidth_{}".format(cur_layer)) - pin_pos.y - via.height/2) < drc("{0}_to_{0}".format(cur_layer)) ):
|
||||
drc_error = 1
|
||||
if drc_error == 0:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
import math
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import layer_properties as layer_props
|
||||
|
|
@ -18,7 +18,7 @@ from tech import preferred_directions
|
|||
from tech import drc
|
||||
|
||||
|
||||
class hierarchical_predecode(design.design):
|
||||
class hierarchical_predecode(design):
|
||||
"""
|
||||
Pre 2x4 and 3x8 and TBD 4x16 decoder shared code.
|
||||
"""
|
||||
|
|
@ -31,7 +31,7 @@ class hierarchical_predecode(design.design):
|
|||
self.cell_height = b.height
|
||||
else:
|
||||
self.cell_height = height
|
||||
|
||||
|
||||
self.column_decoder = column_decoder
|
||||
self.input_and_rail_pos = []
|
||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
|
|
@ -59,13 +59,11 @@ class hierarchical_predecode(design.design):
|
|||
inv_type = "inv_dec"
|
||||
self.and_mod = factory.create(module_type=and_type,
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and_mod)
|
||||
|
||||
# This uses the pinv_dec parameterized cell
|
||||
self.inv = factory.create(module_type=inv_type,
|
||||
height=self.cell_height,
|
||||
size=1)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
""" The general organization is from left to right:
|
||||
|
|
@ -189,13 +187,13 @@ class hierarchical_predecode(design.design):
|
|||
self.route_input_inverters()
|
||||
self.route_input_ands()
|
||||
self.route_output_inverters()
|
||||
self.route_inputs_to_rails()
|
||||
self.route_inputs_to_rails()
|
||||
self.route_output_ands()
|
||||
self.route_vdd_gnd()
|
||||
self.route_supplies()
|
||||
|
||||
def route_inputs_to_rails(self):
|
||||
""" Route the uninverted inputs to the second set of rails """
|
||||
|
||||
|
||||
top_and_gate = self.and_inst[-1]
|
||||
for num in range(self.number_of_inputs):
|
||||
if num == 0:
|
||||
|
|
@ -221,7 +219,7 @@ class hierarchical_predecode(design.design):
|
|||
to_layer=self.bus_layer,
|
||||
offset=[self.input_rails[in_pin].cx(), y_offset],
|
||||
directions= ("H", "H"))
|
||||
|
||||
|
||||
self.add_via_stack_center(from_layer=self.input_layer,
|
||||
to_layer=self.bus_layer,
|
||||
offset=[self.decode_rails[a_pin].cx(), y_offset],
|
||||
|
|
@ -306,7 +304,7 @@ class hierarchical_predecode(design.design):
|
|||
else: # grow the stack down
|
||||
search_id = 2
|
||||
next_id = 0
|
||||
|
||||
|
||||
curr_stack = next(filter(lambda stack: stack[search_id] == cur_layer, layer_stacks), None)
|
||||
|
||||
via = factory.create(module_type="contact",
|
||||
|
|
@ -343,7 +341,7 @@ class hierarchical_predecode(design.design):
|
|||
"""
|
||||
Route the different permutations of the NAND/AND decocer cells.
|
||||
"""
|
||||
|
||||
|
||||
# This 2D array defines the connection mapping
|
||||
and_input_line_combination = self.get_and_input_line_combination()
|
||||
for k in range(self.number_of_outputs):
|
||||
|
|
@ -380,10 +378,10 @@ class hierarchical_predecode(design.design):
|
|||
offset=pin_pos,
|
||||
directions=direction)
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
def route_supplies(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
|
||||
# We may ahve vertical power supply rails
|
||||
# We may have vertical power supply rails
|
||||
if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder:
|
||||
for n in ["vdd", "gnd"]:
|
||||
# This makes a wire from top to bottom for both inv and and gates
|
||||
|
|
@ -397,7 +395,7 @@ class hierarchical_predecode(design.design):
|
|||
height=top_pin.uy() - self.bus_pitch)
|
||||
# This adds power vias at the top of each cell
|
||||
# (except the last to keep them inside the boundary)
|
||||
for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]:
|
||||
for i in [self.inv_inst[0], self.inv_inst[-2], self.and_inst[0], self.and_inst[-2]]:
|
||||
pins = i.get_pins(n)
|
||||
for pin in pins:
|
||||
self.copy_power_pin(pin, loc=pin.uc())
|
||||
|
|
@ -405,8 +403,6 @@ class hierarchical_predecode(design.design):
|
|||
# In other techs, we are using standard cell decoder cells with horizontal power
|
||||
else:
|
||||
for num in range(0, self.number_of_outputs):
|
||||
|
||||
# Route both supplies
|
||||
for n in ["vdd", "gnd"]:
|
||||
and_pins = self.and_inst[num].get_pins(n)
|
||||
for and_pin in and_pins:
|
||||
|
|
@ -415,10 +411,9 @@ class hierarchical_predecode(design.design):
|
|||
end=vector(self.width, and_pin.cy()))
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [self.inv_inst[0].lx() - self.bus_space,
|
||||
self.and_inst[0].lx() - self.bus_space]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.copy_power_pin(and_pin, loc=pin_pos)
|
||||
|
||||
|
||||
|
||||
if n == "vdd":
|
||||
xoffset = self.and_inst[0].lx() - self.bus_space
|
||||
else:
|
||||
xoffset = self.inv_inst[0].lx() - self.bus_space
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(n, pin_pos, start_layer=and_pin.layer)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from .hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from .hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from hierarchical_predecode import hierarchical_predecode
|
||||
from .hierarchical_predecode import hierarchical_predecode
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
import logical_effort
|
||||
from base import design
|
||||
from base import logical_effort
|
||||
from tech import cell_properties as props
|
||||
from tech import spice, parameter
|
||||
|
||||
|
||||
class inv_dec(design.design):
|
||||
class inv_dec(design):
|
||||
"""
|
||||
INV for address decoders.
|
||||
"""
|
||||
|
|
@ -51,12 +51,12 @@ class inv_dec(design.design):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 1
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -5,15 +5,15 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import bitcell_base_array
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from base import vector
|
||||
import debug
|
||||
from tech import layer_properties as layer_props
|
||||
|
||||
|
||||
class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
||||
class local_bitcell_array(bitcell_base_array):
|
||||
"""
|
||||
A local bitcell array is a bitcell array with a wordline driver.
|
||||
This can either be a single aray on its own if there is no hierarchical WL
|
||||
|
|
@ -73,12 +73,10 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
|||
rbl=self.rbl,
|
||||
left_rbl=self.left_rbl,
|
||||
right_rbl=self.right_rbl)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
self.wl_array = factory.create(module_type="wordline_buffer_array",
|
||||
rows=self.rows + 1,
|
||||
cols=self.cols)
|
||||
self.add_mod(self.wl_array)
|
||||
|
||||
def add_pins(self):
|
||||
# Outputs from the wordline driver (by port)
|
||||
|
|
@ -161,17 +159,20 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array):
|
|||
|
||||
def place(self):
|
||||
""" Place the bitcelll array to the right of the wl driver. """
|
||||
# FIXME: Replace this with a tech specific paramter
|
||||
|
||||
# FIXME: Replace this with a tech specific parameter
|
||||
driver_to_array_spacing = 3 * self.m3_pitch
|
||||
|
||||
self.wl_insts[0].place(vector(0, self.cell.height))
|
||||
wl_offset = vector(0, self.bitcell_array.get_replica_bottom())
|
||||
self.wl_insts[0].place(wl_offset)
|
||||
|
||||
self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing,
|
||||
0))
|
||||
bitcell_array_offset = vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0)
|
||||
self.bitcell_array_inst.place(bitcell_array_offset)
|
||||
|
||||
if len(self.all_ports) > 1:
|
||||
self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
|
||||
2 * self.cell.height + self.wl_array.height),
|
||||
wl_offset = vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing,
|
||||
self.bitcell_array.get_replica_bottom() + self.wl_array.height + self.cell.height)
|
||||
self.wl_insts[1].place(wl_offset,
|
||||
mirror="XY")
|
||||
|
||||
self.height = self.bitcell_array.height
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@
|
|||
import sys
|
||||
from tech import drc, parameter
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
import math
|
||||
from math import log,sqrt,ceil
|
||||
import contact
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
|
||||
class multibank(design.design):
|
||||
class multibank(design):
|
||||
"""
|
||||
Dynamically generated a single bank including bitcell array,
|
||||
hierarchical_decoder, precharge, (optional column_mux and column decoder),
|
||||
|
|
@ -172,47 +171,36 @@ class multibank(design.design):
|
|||
|
||||
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
||||
rows=self.num_rows)
|
||||
self.add_mod(self.bitcell_array)
|
||||
|
||||
self.precharge_array = self.mod_precharge_array(columns=self.num_cols)
|
||||
self.add_mod(self.precharge_array)
|
||||
|
||||
if self.col_addr_size > 0:
|
||||
self.column_mux_array = self.mod_column_mux_array(columns=self.num_cols,
|
||||
word_size=self.word_size)
|
||||
self.add_mod(self.column_mux_array)
|
||||
|
||||
|
||||
self.sense_amp_array = self.mod_sense_amp_array(word_size=self.word_size,
|
||||
words_per_row=self.words_per_row)
|
||||
self.add_mod(self.sense_amp_array)
|
||||
|
||||
if self.write_size:
|
||||
self.write_mask_driver_array = self.mod_write_mask_driver_array(columns=self.num_cols,
|
||||
word_size=self.word_size,
|
||||
write_size=self.write_size)
|
||||
self.add_mod(self.write_mask_driver_array)
|
||||
else:
|
||||
self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols,
|
||||
word_size=self.word_size)
|
||||
self.add_mod(self.write_driver_array)
|
||||
|
||||
self.row_decoder = self.mod_decoder(rows=self.num_rows)
|
||||
self.add_mod(self.row_decoder)
|
||||
|
||||
self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols,
|
||||
word_size=self.word_size)
|
||||
self.add_mod(self.tri_gate_array)
|
||||
|
||||
self.wordline_driver = self.mod_wordline_driver(rows=self.num_rows)
|
||||
self.add_mod(self.wordline_driver)
|
||||
|
||||
self.inv = pinv()
|
||||
self.add_mod(self.inv)
|
||||
|
||||
if(self.num_banks > 1):
|
||||
self.bank_select = self.mod_bank_select()
|
||||
self.add_mod(self.bank_select)
|
||||
|
||||
|
||||
def add_bitcell_array(self):
|
||||
|
|
@ -810,7 +798,7 @@ class multibank(design.design):
|
|||
self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
|
||||
# Bring it up to M2 for M2/M3 routing
|
||||
self.add_via(layers=self.m1_stack,
|
||||
offset=in_pin + contact.m1_via.offset,
|
||||
offset=in_pin + self.m1_via.offset,
|
||||
rotate=90)
|
||||
self.add_via(layers=self.m2_stack,
|
||||
offset=in_pin + self.m2m3_via_offset,
|
||||
|
|
@ -823,7 +811,7 @@ class multibank(design.design):
|
|||
rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y)
|
||||
self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
|
||||
self.add_via(layers=self.m1_stack,
|
||||
offset=in_pin + contact.m1_via.offset,
|
||||
offset=in_pin + self.m1_via.offset,
|
||||
rotate=90)
|
||||
self.add_via(layers=self.m2_stack,
|
||||
offset=in_pin + self.m2m3_via_offset,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
from base import design
|
||||
from tech import spice, parameter, drc
|
||||
from tech import cell_properties as props
|
||||
import logical_effort
|
||||
from base import logical_effort
|
||||
|
||||
|
||||
class nand2_dec(design.design):
|
||||
class nand2_dec(design):
|
||||
"""
|
||||
2-input NAND decoder for address decoders.
|
||||
"""
|
||||
|
|
@ -56,12 +56,12 @@ class nand2_dec(design.design):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -96,4 +96,4 @@ class nand2_dec(design.design):
|
|||
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
||||
1,
|
||||
mult)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,13 +5,13 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
from base import design
|
||||
from tech import spice, parameter, drc
|
||||
from tech import cell_properties as props
|
||||
import logical_effort
|
||||
from base import logical_effort
|
||||
|
||||
|
||||
class nand3_dec(design.design):
|
||||
class nand3_dec(design):
|
||||
"""
|
||||
3-input NAND decoder for address decoders.
|
||||
"""
|
||||
|
|
@ -56,12 +56,12 @@ class nand3_dec(design.design):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -96,4 +96,4 @@ class nand3_dec(design.design):
|
|||
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
||||
1,
|
||||
mult)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,13 +5,13 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import design
|
||||
from base import design
|
||||
from tech import spice, parameter, drc
|
||||
from tech import cell_properties as props
|
||||
import logical_effort
|
||||
from base import logical_effort
|
||||
|
||||
|
||||
class nand4_dec(design.design):
|
||||
class nand4_dec(design):
|
||||
"""
|
||||
4-input NAND decoder for address decoders.
|
||||
"""
|
||||
|
|
@ -56,12 +56,12 @@ class nand4_dec(design.design):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -96,4 +96,4 @@ class nand4_dec(design.design):
|
|||
pmos_drain_c = self.drain_c_(self.pmos_width*mult,
|
||||
1,
|
||||
mult)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from bitcell_base_array import bitcell_base_array
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
from tech import drc, spice
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
|
@ -47,7 +47,6 @@ class bitcell_array(bitcell_base_array):
|
|||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
|
|
|
|||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from base import vector
|
||||
from .pgate import *
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pand2(pgate.pgate):
|
||||
class pand2(pgate):
|
||||
"""
|
||||
This is an AND (or NAND) with configurable drive strength.
|
||||
"""
|
||||
|
|
@ -32,16 +32,13 @@ class pand2(pgate.pgate):
|
|||
def create_modules(self):
|
||||
self.nand = factory.create(module_type="pnand2",
|
||||
height=self.height,
|
||||
add_wells=self.vertical)
|
||||
add_wells=False)
|
||||
|
||||
self.inv = factory.create(module_type="pdriver",
|
||||
size_list=[self.size],
|
||||
height=self.height,
|
||||
add_wells=self.add_wells)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
if self.vertical:
|
||||
self.height = 2 * self.nand.height
|
||||
|
|
@ -146,8 +143,8 @@ class pand2(pgate.pgate):
|
|||
offset=pin.center(),
|
||||
width=pin.width(),
|
||||
height=pin.height())
|
||||
|
||||
|
||||
def is_non_inverting(self):
|
||||
"""Return input to output polarity for module"""
|
||||
|
||||
return True
|
||||
|
||||
return True
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from base import vector
|
||||
from .pgate import *
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pand3(pgate.pgate):
|
||||
class pand3(pgate):
|
||||
"""
|
||||
This is a simple buffer used for driving loads.
|
||||
"""
|
||||
|
|
@ -43,9 +43,6 @@ class pand3(pgate.pgate):
|
|||
height=self.height,
|
||||
add_wells=self.add_wells)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
if self.vertical:
|
||||
self.height = 2 * self.nand.height
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from base import vector
|
||||
from .pgate import *
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pand4(pgate.pgate):
|
||||
class pand4(pgate):
|
||||
"""
|
||||
This is a simple buffer used for driving loads.
|
||||
"""
|
||||
|
|
@ -43,9 +43,6 @@ class pand4(pgate.pgate):
|
|||
height=self.height,
|
||||
add_wells=self.add_wells)
|
||||
|
||||
self.add_mod(self.nand)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
if self.vertical:
|
||||
self.height = 2 * self.nand.height
|
||||
|
|
@ -162,4 +159,3 @@ class pand4(pgate.pgate):
|
|||
slew=nand_delay.slew,
|
||||
load=load)
|
||||
return nand_delay + inv_delay
|
||||
|
||||
|
|
@ -5,18 +5,17 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import contact
|
||||
import debug
|
||||
from base import logical_effort
|
||||
from base import vector
|
||||
from tech import drc, parameter, layer
|
||||
from tech import cell_properties as props
|
||||
from vector import vector
|
||||
from ptx import ptx
|
||||
from globals import OPTS
|
||||
import logical_effort
|
||||
import bitcell_base
|
||||
from .ptx import ptx
|
||||
from .bitcell_base import bitcell_base
|
||||
|
||||
|
||||
class pbitcell(bitcell_base.bitcell_base):
|
||||
class pbitcell(bitcell_base):
|
||||
"""
|
||||
This module implements a parametrically sized multi-port bitcell,
|
||||
with a variable number of read/write, write, and read ports
|
||||
|
|
@ -33,7 +32,19 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
self.mirror = props.bitcell_1port.mirror
|
||||
self.end_caps = props.bitcell_1port.end_caps
|
||||
|
||||
bitcell_base.bitcell_base.__init__(self, name)
|
||||
self.wl_layer = "m1"
|
||||
self.wl_dir = "H"
|
||||
|
||||
self.bl_layer = "m2"
|
||||
self.bl_dir = "V"
|
||||
|
||||
self.vdd_layer = "m1"
|
||||
self.vdd_dir = "H"
|
||||
|
||||
self.gnd_layer = "m1"
|
||||
self.gnd_dir = "H"
|
||||
|
||||
bitcell_base.__init__(self, name)
|
||||
fmt_str = "{0} rw ports, {1} w ports and {2} r ports"
|
||||
info_string = fmt_str.format(self.num_rw_ports,
|
||||
self.num_w_ports,
|
||||
|
|
@ -87,7 +98,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
|
||||
self.route_wordlines()
|
||||
self.route_bitlines()
|
||||
self.route_supply()
|
||||
self.route_supplies()
|
||||
|
||||
if self.replica_bitcell:
|
||||
self.route_rbc_short()
|
||||
|
|
@ -179,26 +190,21 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
# create ptx for inverter transistors
|
||||
self.inverter_nmos = ptx(width=inverter_nmos_width,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.inverter_nmos)
|
||||
|
||||
self.inverter_pmos = ptx(width=inverter_pmos_width,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.inverter_pmos)
|
||||
|
||||
# create ptx for readwrite transitors
|
||||
self.readwrite_nmos = ptx(width=readwrite_nmos_width,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.readwrite_nmos)
|
||||
|
||||
# create ptx for write transitors
|
||||
self.write_nmos = ptx(width=write_nmos_width,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.write_nmos)
|
||||
|
||||
# create ptx for read transistors
|
||||
self.read_nmos = ptx(width=read_nmos_width,
|
||||
tx_type="nmos")
|
||||
self.add_mod(self.read_nmos)
|
||||
|
||||
def calculate_spacing(self):
|
||||
""" Calculate transistor spacings """
|
||||
|
|
@ -215,20 +221,20 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
|
||||
# y-offset for the access transistor's gate contact
|
||||
self.gate_contact_yoffset = max_contact_extension + self.m2_space \
|
||||
+ 0.5 * max(contact.poly_contact.height, contact.m1_via.height)
|
||||
+ 0.5 * max(self.poly_contact.height, self.m1_via.height)
|
||||
|
||||
# y-position of access transistors
|
||||
self.port_ypos = self.m1_space + 0.5 * contact.m1_via.height + self.gate_contact_yoffset
|
||||
self.port_ypos = self.m1_space + 0.5 * self.m1_via.height + self.gate_contact_yoffset
|
||||
|
||||
# y-position of inverter nmos
|
||||
self.inverter_nmos_ypos = self.port_ypos
|
||||
|
||||
# spacing between ports (same for read/write and write ports)
|
||||
self.bitline_offset = -0.5 * self.readwrite_nmos.active_width \
|
||||
+ 0.5 * contact.m1_via.height \
|
||||
+ 0.5 * self.m1_via.height \
|
||||
+ self.m2_space + self.m2_width
|
||||
m2_constraint = self.bitline_offset + self.m2_space \
|
||||
+ 0.5 * contact.m1_via.height \
|
||||
+ 0.5 * self.m1_via.height \
|
||||
- 0.5 * self.readwrite_nmos.active_width
|
||||
self.write_port_spacing = max(self.active_space,
|
||||
self.m1_space,
|
||||
|
|
@ -236,7 +242,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
self.read_port_spacing = self.bitline_offset + self.m2_space
|
||||
|
||||
# spacing between cross coupled inverters
|
||||
self.inverter_to_inverter_spacing = contact.poly_contact.width + self.m1_space
|
||||
self.inverter_to_inverter_spacing = self.poly_contact.width + self.m1_space
|
||||
|
||||
# calculations related to inverter connections
|
||||
inverter_pmos_contact_extension = 0.5 * \
|
||||
|
|
@ -245,19 +251,19 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height)
|
||||
self.inverter_gap = max(self.poly_to_active,
|
||||
self.m1_space + inverter_nmos_contact_extension) \
|
||||
+ self.poly_to_contact + 2 * contact.poly_contact.width \
|
||||
+ self.poly_to_contact + 2 * self.poly_contact.width \
|
||||
+ self.m1_space + inverter_pmos_contact_extension
|
||||
self.cross_couple_lower_ypos = self.inverter_nmos_ypos \
|
||||
+ self.inverter_nmos.active_height \
|
||||
+ max(self.poly_to_active,
|
||||
self.m1_space + inverter_nmos_contact_extension) \
|
||||
+ 0.5 * contact.poly_contact.width
|
||||
+ 0.5 * self.poly_contact.width
|
||||
self.cross_couple_upper_ypos = self.inverter_nmos_ypos \
|
||||
+ self.inverter_nmos.active_height \
|
||||
+ max(self.poly_to_active,
|
||||
self.m1_space + inverter_nmos_contact_extension) \
|
||||
+ self.poly_to_contact \
|
||||
+ 1.5 * contact.poly_contact.width
|
||||
+ 1.5 * self.poly_contact.width
|
||||
|
||||
# spacing between wordlines (and gnd)
|
||||
self.m1_offset = -0.5 * self.m1_width
|
||||
|
|
@ -293,7 +299,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
(self.write_nmos.active_width + self.write_port_spacing) \
|
||||
- self.num_r_ports * \
|
||||
(self.read_port_width + self.read_port_spacing) \
|
||||
- self.bitline_offset - 0.5 * contact.m1_via.width
|
||||
- self.bitline_offset - 0.5 * self.m1_via.width
|
||||
|
||||
self.width = -2 * self.leftmost_xpos
|
||||
self.height = self.topmost_ypos - self.botmost_ypos
|
||||
|
|
@ -383,14 +389,14 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
# add contacts to connect gate poly to drain/source
|
||||
# metal1 (to connect Q to Q_bar)
|
||||
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
||||
+ 0.5 * contact.poly_contact.height,
|
||||
+ 0.5 * self.poly_contact.height,
|
||||
self.cross_couple_upper_ypos)
|
||||
self.add_via_center(layers=self.poly_stack,
|
||||
offset=contact_offset_left,
|
||||
directions=("H", "H"))
|
||||
|
||||
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
||||
- 0.5*contact.poly_contact.height,
|
||||
- 0.5*self.poly_contact.height,
|
||||
self.cross_couple_lower_ypos)
|
||||
self.add_via_center(layers=self.poly_stack,
|
||||
offset=contact_offset_right,
|
||||
|
|
@ -407,24 +413,24 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
if OPTS.use_pex:
|
||||
# add labels to cross couple inverter for extracted simulation
|
||||
contact_offset_left_output = vector(self.inverter_nmos_left.get_pin("D").rc().x \
|
||||
+ 0.5 * contact.poly.height,
|
||||
+ 0.5 * self.poly.height,
|
||||
self.cross_couple_upper_ypos)
|
||||
|
||||
contact_offset_right_output = vector(self.inverter_nmos_right.get_pin("S").lc().x \
|
||||
- 0.5*contact.poly.height,
|
||||
- 0.5*self.poly.height,
|
||||
self.cross_couple_lower_ypos)
|
||||
self.add_pex_labels(contact_offset_left_output, contact_offset_right_output)
|
||||
|
||||
def route_rails(self):
|
||||
""" Adds gnd and vdd rails and connects them to the inverters """
|
||||
|
||||
# Add rails for vdd and gnd
|
||||
gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
|
||||
self.gnd_position = vector(0, gnd_ypos)
|
||||
self.add_rect_center(layer="m1",
|
||||
offset=self.gnd_position,
|
||||
width=self.width)
|
||||
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
|
||||
|
||||
self.add_layout_pin_rect_center(text="gnd",
|
||||
layer="m1",
|
||||
offset=self.gnd_position,
|
||||
width=self.width)
|
||||
|
||||
vdd_ypos = self.inverter_nmos_ypos \
|
||||
+ self.inverter_nmos.active_height \
|
||||
|
|
@ -432,10 +438,10 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
+ self.inverter_pmos.active_height \
|
||||
+ self.vdd_offset
|
||||
self.vdd_position = vector(0, vdd_ypos)
|
||||
self.add_rect_center(layer="m1",
|
||||
offset=self.vdd_position,
|
||||
width=self.width)
|
||||
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer="m1",
|
||||
offset=self.vdd_position,
|
||||
width=self.width)
|
||||
|
||||
def create_readwrite_ports(self):
|
||||
"""
|
||||
|
|
@ -475,6 +481,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
self.connect_inst([self.Q_bar,
|
||||
self.rw_wl_names[k], br_name, "gnd"])
|
||||
|
||||
|
||||
def place_readwrite_ports(self):
|
||||
""" Places read/write ports in the bit cell """
|
||||
# define read/write transistor variables as empty arrays
|
||||
|
|
@ -529,6 +536,21 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
offset=self.rwbr_positions[k],
|
||||
height=self.height)
|
||||
|
||||
if self.dummy_bitcell:
|
||||
bl_name = self.rw_bl_names[k]
|
||||
br_name = self.rw_br_names[k]
|
||||
bl_name += "_noconn"
|
||||
br_name += "_noconn"
|
||||
|
||||
# This helps with LVS matching in klayout
|
||||
drain_pin = self.readwrite_nmos_left[k].get_pin("S")
|
||||
self.add_label(bl_name, drain_pin.layer, drain_pin.center())
|
||||
|
||||
# This helps with LVS matching in klayout
|
||||
source_pin = self.readwrite_nmos_right[k].get_pin("D")
|
||||
self.add_label(br_name, source_pin.layer, source_pin.center())
|
||||
|
||||
|
||||
# update furthest left and right transistor edges
|
||||
self.left_building_edge = left_readwrite_transistor_xpos
|
||||
self.right_building_edge = right_readwrite_transistor_xpos \
|
||||
|
|
@ -626,6 +648,20 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
offset=self.wbr_positions[k],
|
||||
height=self.height)
|
||||
|
||||
if self.dummy_bitcell:
|
||||
bl_name = self.w_bl_names[k]
|
||||
br_name = self.w_br_names[k]
|
||||
bl_name += "_noconn"
|
||||
br_name += "_noconn"
|
||||
|
||||
# This helps with LVS matching in klayout
|
||||
drain_pin = self.write_nmos_left[k].get_pin("S")
|
||||
self.add_label(bl_name, drain_pin.layer, drain_pin.center())
|
||||
|
||||
# This helps with LVS matching in klayout
|
||||
source_pin = self.write_nmos_right[k].get_pin("D")
|
||||
self.add_label(br_name, source_pin.layer, source_pin.center())
|
||||
|
||||
# update furthest left and right transistor edges
|
||||
self.left_building_edge = left_write_transistor_xpos
|
||||
self.right_building_edge = right_write_transistor_xpos \
|
||||
|
|
@ -753,6 +789,20 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
offset=self.rbr_positions[k],
|
||||
height=self.height)
|
||||
|
||||
if self.dummy_bitcell:
|
||||
bl_name = self.r_bl_names[k]
|
||||
br_name = self.r_br_names[k]
|
||||
bl_name += "_noconn"
|
||||
br_name += "_noconn"
|
||||
|
||||
# This helps with LVS matching in klayout
|
||||
drain_pin = self.read_access_nmos_left[k].get_pin("S")
|
||||
self.add_label(bl_name, drain_pin.layer, drain_pin.center())
|
||||
|
||||
# This helps with LVS matching in klayout
|
||||
source_pin = self.read_access_nmos_right[k].get_pin("D")
|
||||
self.add_label(br_name, source_pin.layer, source_pin.center())
|
||||
|
||||
def route_wordlines(self):
|
||||
""" Routes gate of transistors to their respective wordlines """
|
||||
port_transistors = []
|
||||
|
|
@ -845,7 +895,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
directions="nonpref")
|
||||
|
||||
self.add_path("m2",
|
||||
[port_contact_offest, bl_offset], width=contact.m1_via.height)
|
||||
[port_contact_offest, bl_offset], width=self.m1_via.height)
|
||||
|
||||
for k in range(self.total_ports):
|
||||
port_contact_offest = right_port_transistors[k].get_pin("D").center()
|
||||
|
|
@ -858,9 +908,9 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
directions="nonpref")
|
||||
|
||||
self.add_path("m2",
|
||||
[port_contact_offest, br_offset], width=contact.m1_via.height)
|
||||
[port_contact_offest, br_offset], width=self.m1_via.height)
|
||||
|
||||
def route_supply(self):
|
||||
def route_supplies(self):
|
||||
""" Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """
|
||||
# route inverter nmos and read-access nmos to gnd
|
||||
nmos_contact_positions = []
|
||||
|
|
@ -877,9 +927,9 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
|
||||
|
||||
if position.x > 0:
|
||||
contact_correct = 0.5 * contact.m1_via.height
|
||||
contact_correct = 0.5 * self.m1_via.height
|
||||
else:
|
||||
contact_correct = -0.5 * contact.m1_via.height
|
||||
contact_correct = -0.5 * self.m1_via.height
|
||||
supply_offset = vector(position.x + contact_correct,
|
||||
self.gnd_position.y)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
|
|
@ -946,7 +996,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
"""
|
||||
# add poly to metal1 contacts for gates of the inverters
|
||||
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \
|
||||
- self.poly_to_contact - 0.5*contact.poly_contact.width,
|
||||
- self.poly_to_contact - 0.5*self.poly_contact.width,
|
||||
self.cross_couple_upper_ypos)
|
||||
self.add_via_center(layers=self.poly_stack,
|
||||
offset=left_storage_contact,
|
||||
|
|
@ -954,7 +1004,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
|
||||
|
||||
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
|
||||
+ self.poly_to_contact + 0.5*contact.poly_contact.width,
|
||||
+ self.poly_to_contact + 0.5*self.poly_contact.width,
|
||||
self.cross_couple_upper_ypos)
|
||||
self.add_via_center(layers=self.poly_stack,
|
||||
offset=right_storage_contact,
|
||||
|
|
@ -1013,7 +1063,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
well_height = max_nmos_well_height + self.port_ypos \
|
||||
- self.nwell_enclose_active - self.gnd_position.y
|
||||
# FIXME fudge factor xpos
|
||||
well_width = self.width + 2*self.nwell_enclose_active
|
||||
well_width = self.width + 2 * self.nwell_enclose_active
|
||||
offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos)
|
||||
self.add_rect(layer="pwell",
|
||||
offset=offset,
|
||||
|
|
@ -1141,12 +1191,12 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
# min size NMOS gate load
|
||||
read_port_load = self.num_r_ports / 2
|
||||
total_load = load + read_port_load + write_port_load
|
||||
return logical_effort.logical_effort('bitline',
|
||||
size,
|
||||
cin,
|
||||
load + read_port_load,
|
||||
parasitic_delay,
|
||||
False)
|
||||
return logical_effort('bitline',
|
||||
size,
|
||||
cin,
|
||||
load + read_port_load,
|
||||
parasitic_delay,
|
||||
False)
|
||||
|
||||
def input_load(self):
|
||||
""" Return the relative capacitance of the access transistor gates """
|
||||
|
|
@ -1163,7 +1213,7 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
return
|
||||
|
||||
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
|
||||
|
||||
|
||||
# Edges added wl->bl, wl->br for every port except write ports
|
||||
rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names)
|
||||
r_pin_names = zip(self.rw_wl_names, self.rw_bl_names, self.rw_br_names)
|
||||
|
|
@ -1172,4 +1222,3 @@ class pbitcell(bitcell_base.bitcell_base):
|
|||
for wl, bl, br in pin_zip:
|
||||
graph.add_edge(pin_dict[wl], pin_dict[bl], self)
|
||||
graph.add_edge(pin_dict[wl], pin_dict[br], self)
|
||||
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from base import vector
|
||||
from .pgate import *
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pbuf(pgate.pgate):
|
||||
class pbuf(pgate):
|
||||
"""
|
||||
This is a simple buffer used for driving loads.
|
||||
"""
|
||||
|
|
@ -52,13 +52,11 @@ class pbuf(pgate.pgate):
|
|||
self.inv1 = factory.create(module_type="pinv",
|
||||
size=input_size,
|
||||
height=self.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = factory.create(module_type="pinv",
|
||||
size=self.size,
|
||||
height=self.height,
|
||||
add_wells=False)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def create_insts(self):
|
||||
self.inv1_inst = self.add_inst(name="buf_inv1",
|
||||
|
|
@ -96,4 +94,3 @@ class pbuf(pgate.pgate):
|
|||
offset=a_pin.center(),
|
||||
width=a_pin.width(),
|
||||
height=a_pin.height())
|
||||
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
from vector import vector
|
||||
import pgate
|
||||
from base import vector
|
||||
from .pgate import *
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pbuf_dec(pgate.pgate):
|
||||
class pbuf_dec(pgate):
|
||||
"""
|
||||
This is a simple buffer used for driving wordlines.
|
||||
"""
|
||||
|
|
@ -25,7 +25,7 @@ class pbuf_dec(pgate.pgate):
|
|||
self.height = height
|
||||
|
||||
# Creates the netlist and layout
|
||||
pgate.pgate.__init__(self, name, height)
|
||||
pgate.__init__(self, name, height)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
|
|
@ -52,12 +52,10 @@ class pbuf_dec(pgate.pgate):
|
|||
self.inv1 = factory.create(module_type="pinv_dec",
|
||||
size=input_size,
|
||||
height=self.height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = factory.create(module_type="pinv_dec",
|
||||
size=self.size,
|
||||
height=self.height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def create_insts(self):
|
||||
self.inv1_inst = self.add_inst(name="buf_inv1",
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import pgate
|
||||
from vector import vector
|
||||
from .pgate import *
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class pdriver(pgate.pgate):
|
||||
class pdriver(pgate):
|
||||
"""
|
||||
This instantiates an even or odd number of inverters
|
||||
sized for driving a load.
|
||||
|
|
@ -93,7 +93,6 @@ class pdriver(pgate.pgate):
|
|||
height=self.height,
|
||||
add_wells=self.add_wells)
|
||||
self.inv_list.append(temp_inv)
|
||||
self.add_mod(temp_inv)
|
||||
|
||||
def create_insts(self):
|
||||
self.inv_inst_list = []
|
||||
|
|
@ -5,20 +5,20 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import contact
|
||||
import design
|
||||
from base import design
|
||||
from base import vector
|
||||
import debug
|
||||
import math
|
||||
from bisect import bisect_left
|
||||
from tech import layer, drc
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
if cell_props.ptx.bin_spice_models:
|
||||
from tech import nmos_bins, pmos_bins
|
||||
|
||||
|
||||
class pgate(design.design):
|
||||
class pgate(design):
|
||||
"""
|
||||
This is a module that implements some shared
|
||||
functions for parameterized gates.
|
||||
|
|
@ -113,14 +113,14 @@ class pgate(design.design):
|
|||
left_gate_offset = vector(nmos_gate_pin.lx(), ypos)
|
||||
|
||||
# Center is completely symmetric.
|
||||
contact_width = contact.poly_contact.width
|
||||
contact_width = self.poly_contact.width
|
||||
|
||||
if position == "center":
|
||||
contact_offset = left_gate_offset \
|
||||
+ vector(0.5 * self.poly_width, 0)
|
||||
elif position == "farleft":
|
||||
contact_offset = left_gate_offset \
|
||||
- vector(0.5 * contact.poly_contact.width, 0)
|
||||
- vector(0.5 * self.poly_contact.width, 0)
|
||||
elif position == "left":
|
||||
contact_offset = left_gate_offset \
|
||||
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
|
||||
|
|
@ -146,7 +146,7 @@ class pgate(design.design):
|
|||
+ left_gate_offset.scale(0.5, 0)
|
||||
self.add_rect_center(layer="poly",
|
||||
offset=mid_point,
|
||||
height=contact.poly_contact.first_layer_width,
|
||||
height=self.poly_contact.first_layer_width,
|
||||
width=left_gate_offset.x - contact_offset.x)
|
||||
|
||||
return via
|
||||
|
|
@ -208,12 +208,15 @@ class pgate(design.design):
|
|||
# from the top of the well
|
||||
# OR align the active with the top of PMOS active.
|
||||
max_y_offset = self.height + 0.5 * self.m1_width
|
||||
contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
|
||||
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.nwell_enclose_active)
|
||||
contact_yoffset = min(self.height - 0.5 * self.implant_width,
|
||||
self.get_tx_insts("pmos")[0].uy()) \
|
||||
- pmos.active_contact.first_layer_height \
|
||||
- self.implant_enclose_active
|
||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||
# Offset by half a contact in x and y
|
||||
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
|
||||
0.5 * pmos.active_contact.first_layer_height)
|
||||
# This over-rides the default one with a custom direction
|
||||
self.nwell_contact = self.add_via_center(layers=layer_stack,
|
||||
offset=contact_offset,
|
||||
implant_type="n",
|
||||
|
|
@ -276,24 +279,34 @@ class pgate(design.design):
|
|||
rightx=rightx,
|
||||
topy=self.height)
|
||||
|
||||
try:
|
||||
ntap_insts = [self.nwell_contact]
|
||||
self.add_enclosure(ntap_insts,
|
||||
layer="nimplant",
|
||||
extend=self.implant_enclose_active,
|
||||
rightx=self.width,
|
||||
topy=self.height)
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
ptap_insts = [self.pwell_contact]
|
||||
self.add_enclosure(ptap_insts,
|
||||
layer="pimplant",
|
||||
extend=self.implant_enclose_active,
|
||||
rightx=self.width,
|
||||
boty=0)
|
||||
except AttributeError:
|
||||
pass
|
||||
self.add_rect(layer="pimplant",
|
||||
offset=vector(0, self.height - 0.5 * self.implant_width),
|
||||
width=self.width,
|
||||
height=self.implant_width)
|
||||
self.add_rect(layer="nimplant",
|
||||
offset=vector(0, -0.5 * self.implant_width),
|
||||
width=self.width,
|
||||
height=self.implant_width)
|
||||
|
||||
|
||||
# try:
|
||||
# ntap_insts = [self.nwell_contact]
|
||||
# self.add_enclosure(ntap_insts,
|
||||
# layer="nimplant",
|
||||
# extend=self.implant_enclose_active,
|
||||
# rightx=self.width,
|
||||
# topy=self.height)
|
||||
# except AttributeError:
|
||||
# pass
|
||||
# try:
|
||||
# ptap_insts = [self.pwell_contact]
|
||||
# self.add_enclosure(ptap_insts,
|
||||
# layer="pimplant",
|
||||
# extend=self.implant_enclose_active,
|
||||
# rightx=self.width,
|
||||
# boty=0)
|
||||
# except AttributeError:
|
||||
# pass
|
||||
|
||||
def add_pwell_contact(self, nmos, nmos_pos):
|
||||
""" Add an pwell contact next to the given nmos device. """
|
||||
|
|
@ -303,11 +316,9 @@ class pgate(design.design):
|
|||
# To the right a spacing away from the nmos right active edge
|
||||
contact_xoffset = nmos_pos.x + nmos.active_width \
|
||||
+ self.active_space
|
||||
# Must be at least an well enclosure of active up
|
||||
# from the bottom of the well
|
||||
contact_yoffset = max(nmos_pos.y,
|
||||
self.nwell_enclose_active \
|
||||
- nmos.active_contact.first_layer_height / 2)
|
||||
# Allow an nimplant below it under the rail
|
||||
contact_yoffset = max(0.5 * self.implant_width + self.implant_enclose_active,
|
||||
self.get_tx_insts("nmos")[0].by())
|
||||
contact_offset = vector(contact_xoffset, contact_yoffset)
|
||||
|
||||
# Offset by half a contact
|
||||
|
|
@ -362,7 +373,7 @@ class pgate(design.design):
|
|||
# It was already set or is left as default (minimum)
|
||||
# Width is determined by well contact and spacing and allowing a supply via between each cell
|
||||
if self.add_wells:
|
||||
width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * contact.m1_via.width
|
||||
width = max(self.nwell_contact.rx(), self.pwell_contact.rx()) + self.m1_space + 0.5 * self.m1_via.width
|
||||
# Height is an input parameter, so it is not recomputed.
|
||||
else:
|
||||
max_active_xoffset = self.find_highest_layer_coords("active").x
|
||||
|
|
@ -5,22 +5,21 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import contact
|
||||
import pgate
|
||||
import debug
|
||||
from .pgate import *
|
||||
from base import vector
|
||||
from base import logical_effort
|
||||
from base.utils import round_to_grid
|
||||
from base.errors import drc_error
|
||||
import operator
|
||||
from tech import drc, parameter, spice
|
||||
from vector import vector
|
||||
from math import ceil
|
||||
from globals import OPTS
|
||||
from utils import round_to_grid
|
||||
import logical_effort
|
||||
from sram_factory import factory
|
||||
from errors import drc_error
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class pinv(pgate.pgate):
|
||||
class pinv(pgate):
|
||||
"""
|
||||
Pinv generates gds of a parametrically sized inverter. The
|
||||
size is specified as the drive size (relative to minimum NMOS) and
|
||||
|
|
@ -89,8 +88,8 @@ class pinv(pgate.pgate):
|
|||
self.nmos_width = self.nmos_size * drc("minwidth_tx")
|
||||
self.pmos_width = self.pmos_size * drc("minwidth_tx")
|
||||
if cell_props.ptx.bin_spice_models:
|
||||
(self.nmos_width, self.tx_mults) = pgate.pgate.best_bin("nmos", self.nmos_width)
|
||||
(self.pmos_width, self.tx_mults) = pgate.pgate.best_bin("pmos", self.pmos_width)
|
||||
(self.nmos_width, self.tx_mults) = pgate.best_bin("nmos", self.nmos_width)
|
||||
(self.pmos_width, self.tx_mults) = pgate.best_bin("pmos", self.pmos_width)
|
||||
return
|
||||
|
||||
# Do a quick sanity check and bail if unlikely feasible height
|
||||
|
|
@ -106,8 +105,8 @@ class pinv(pgate.pgate):
|
|||
tx_type="pmos")
|
||||
tx_height = nmos.poly_height + pmos.poly_height
|
||||
# rotated m1 pitch or poly to active spacing
|
||||
min_channel = max(contact.poly_contact.width + self.m1_space,
|
||||
contact.poly_contact.width + 2 * self.poly_to_active)
|
||||
min_channel = max(self.poly_contact.width + self.m1_space,
|
||||
self.poly_contact.width + 2 * self.poly_to_active)
|
||||
|
||||
total_height = tx_height + min_channel + 2 * self.top_bottom_space
|
||||
# debug.check(self.height > total_height,
|
||||
|
|
@ -207,7 +206,6 @@ class pinv(pgate.pgate):
|
|||
add_drain_contact=self.route_layer,
|
||||
connect_poly=True,
|
||||
connect_drain_active=True)
|
||||
self.add_mod(self.nmos)
|
||||
|
||||
self.pmos = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -217,7 +215,6 @@ class pinv(pgate.pgate):
|
|||
add_drain_contact=self.route_layer,
|
||||
connect_poly=True,
|
||||
connect_drain_active=True)
|
||||
self.add_mod(self.pmos)
|
||||
|
||||
def create_ptx(self):
|
||||
"""
|
||||
|
|
@ -324,12 +321,12 @@ class pinv(pgate.pgate):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 1
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -337,30 +334,29 @@ class pinv(pgate.pgate):
|
|||
Overrides base class function.
|
||||
"""
|
||||
self.add_graph_edges(graph, port_nets)
|
||||
|
||||
|
||||
def is_non_inverting(self):
|
||||
"""Return input to output polarity for module"""
|
||||
|
||||
return False
|
||||
return False
|
||||
|
||||
def get_on_resistance(self):
|
||||
"""On resistance of pinv, defined by single nmos"""
|
||||
is_nchannel = True
|
||||
stack = 1
|
||||
is_cell = False
|
||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||
|
||||
def get_input_capacitance(self):
|
||||
"""Input cap of input, passes width of gates to gate cap function"""
|
||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||
|
||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||
|
||||
def get_intrinsic_capacitance(self):
|
||||
"""Get the drain capacitances of the TXs in the gate."""
|
||||
nmos_stack = 1
|
||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||
nmos_stack,
|
||||
self.tx_mults)
|
||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
1,
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,17 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import contact
|
||||
import pinv
|
||||
import debug
|
||||
from base import vector
|
||||
from .pinv import pinv
|
||||
from tech import drc, parameter, layer
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class pinv_dec(pinv.pinv):
|
||||
class pinv_dec(pinv):
|
||||
"""
|
||||
This is another version of pinv but with layout for the decoder.
|
||||
Other stuff is the same (netlist, sizes, etc.)
|
||||
|
|
@ -78,7 +77,7 @@ class pinv_dec(pinv.pinv):
|
|||
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
|
||||
|
||||
# Center is completely symmetric.
|
||||
contact_width = contact.poly_contact.width
|
||||
contact_width = self.poly_contact.width
|
||||
contact_offset = nmos_gate_pin.lc() \
|
||||
- vector(self.poly_extend_active + 0.5 * contact_width, 0)
|
||||
via = self.add_via_stack_center(from_layer="poly",
|
||||
|
|
@ -126,7 +125,7 @@ class pinv_dec(pinv.pinv):
|
|||
y_offset = (0.5 * (self.height - self.nmos.width) + self.nmos.width) * 0.9
|
||||
|
||||
# offset so that the input contact is over from the left edge by poly spacing
|
||||
x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space
|
||||
x_offset = self.nmos.active_offset.y + self.poly_contact.width + self.poly_space
|
||||
self.nmos_pos = vector(x_offset, y_offset)
|
||||
self.nmos_inst.place(self.nmos_pos,
|
||||
rotate=270)
|
||||
|
|
@ -192,20 +191,20 @@ class pinv_dec(pinv.pinv):
|
|||
|
||||
source_pos = self.pmos_inst.get_pin("S").center()
|
||||
contact_pos = vector(source_pos.x, self.height)
|
||||
self.nwell_contact = self.add_via_center(layers=self.active_stack,
|
||||
offset=contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_via_center(layers=self.active_stack,
|
||||
offset=contact_pos,
|
||||
implant_type="n",
|
||||
well_type="n")
|
||||
self.add_via_stack_center(offset=contact_pos,
|
||||
from_layer=self.active_stack[2],
|
||||
to_layer=self.supply_layer)
|
||||
|
||||
source_pos = self.nmos_inst.get_pin("S").center()
|
||||
contact_pos = vector(source_pos.x, self.height)
|
||||
self.pwell_contact= self.add_via_center(layers=self.active_stack,
|
||||
offset=contact_pos,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.add_via_center(layers=self.active_stack,
|
||||
offset=contact_pos,
|
||||
implant_type="p",
|
||||
well_type="p")
|
||||
self.add_via_stack_center(offset=contact_pos,
|
||||
from_layer=self.active_stack[2],
|
||||
to_layer=self.supply_layer)
|
||||
|
|
@ -6,12 +6,12 @@
|
|||
# All rights reserved.
|
||||
#
|
||||
import debug
|
||||
import pgate
|
||||
from vector import vector
|
||||
from .pgate import *
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from tech import layer
|
||||
|
||||
class pinvbuf(pgate.pgate):
|
||||
class pinvbuf(pgate):
|
||||
"""
|
||||
This is a simple inverter/buffer used for driving loads. It is
|
||||
used in the column decoder for 1:2 decoding and as the clock buffer.
|
||||
|
|
@ -65,17 +65,14 @@ class pinvbuf(pgate.pgate):
|
|||
self.inv = factory.create(module_type="pinv",
|
||||
size=input_size,
|
||||
height=self.row_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.inv1 = factory.create(module_type="pinv",
|
||||
size=self.predriver_size,
|
||||
height=self.row_height)
|
||||
self.add_mod(self.inv1)
|
||||
|
||||
self.inv2 = factory.create(module_type="pinv",
|
||||
size=self.size,
|
||||
height=self.row_height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
def create_insts(self):
|
||||
# Create INV1 (capacitance shield)
|
||||
|
|
@ -5,17 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import pgate
|
||||
from .pgate import *
|
||||
import debug
|
||||
from tech import drc, parameter, spice
|
||||
from vector import vector
|
||||
import logical_effort
|
||||
from base import vector
|
||||
from base import logical_effort
|
||||
from sram_factory import factory
|
||||
import contact
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class pnand2(pgate.pgate):
|
||||
class pnand2(pgate):
|
||||
"""
|
||||
This module generates gds of a parametrically sized 2-input nand.
|
||||
This model use ptx to generate a 2-input nand within a cetrain height.
|
||||
|
|
@ -79,7 +78,6 @@ class pnand2(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact="active")
|
||||
self.add_mod(self.nmos_left)
|
||||
|
||||
self.nmos_right = factory.create(module_type="ptx",
|
||||
width=self.nmos_width,
|
||||
|
|
@ -87,7 +85,6 @@ class pnand2(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact="active",
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.nmos_right)
|
||||
|
||||
self.pmos_left = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -95,7 +92,6 @@ class pnand2(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_left)
|
||||
|
||||
self.pmos_right = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -103,7 +99,6 @@ class pnand2(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_right)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Pre-compute some handy layout parameters. """
|
||||
|
|
@ -179,11 +174,11 @@ class pnand2(pgate.pgate):
|
|||
# Top of NMOS drain
|
||||
bottom_pin = self.nmos1_inst.get_pin("D")
|
||||
# active contact metal to poly contact metal spacing
|
||||
active_contact_to_poly_contact = bottom_pin.uy() + self.route_layer_space + 0.5 * contact.poly_contact.second_layer_height
|
||||
active_contact_to_poly_contact = bottom_pin.uy() + self.route_layer_space + 0.5 * self.poly_contact.second_layer_height
|
||||
# active diffusion to poly contact spacing
|
||||
# doesn't use nmos uy because that is calculated using offset + poly height
|
||||
active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * self.poly_contact.first_layer_height
|
||||
active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width
|
||||
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||
active_to_poly_contact,
|
||||
|
|
@ -197,9 +192,9 @@ class pnand2(pgate.pgate):
|
|||
|
||||
self.inputB_yoffset = self.inputA_yoffset + 2 * self.m3_pitch
|
||||
# # active contact metal to poly contact metal spacing
|
||||
# active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * contact.poly_contact.second_layer_height
|
||||
# active_contact_to_poly_contact = self.output_yoffset - self.route_layer_space - 0.5 * self.poly_contact.second_layer_height
|
||||
# active_bottom = self.pmos1_inst.by()
|
||||
# active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * contact.poly_contact.first_layer_height
|
||||
# active_to_poly_contact = active_bottom - self.poly_to_active - 0.5 * self.poly_contact.first_layer_height
|
||||
# active_to_poly_contact2 = active_bottom - self.poly_contact_to_gate - 0.5 * self.route_layer_width
|
||||
# self.inputB_yoffset = min(active_contact_to_poly_contact,
|
||||
# active_to_poly_contact,
|
||||
|
|
@ -219,7 +214,7 @@ class pnand2(pgate.pgate):
|
|||
""" Route the Z output """
|
||||
|
||||
# One routing track layer below the PMOS contacts
|
||||
route_layer_offset = 0.5 * contact.poly_contact.second_layer_height + self.route_layer_space
|
||||
route_layer_offset = 0.5 * self.poly_contact.second_layer_height + self.route_layer_space
|
||||
self.output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset
|
||||
|
||||
|
||||
|
|
@ -301,12 +296,12 @@ class pnand2(pgate.pgate):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 2
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -317,28 +312,26 @@ class pnand2(pgate.pgate):
|
|||
|
||||
def is_non_inverting(self):
|
||||
"""Return input to output polarity for module"""
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_on_resistance(self):
|
||||
"""On resistance of pnand, defined by stacked NMOS"""
|
||||
is_nchannel = True
|
||||
stack = 2
|
||||
is_cell = False
|
||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||
|
||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||
|
||||
def get_input_capacitance(self):
|
||||
"""Input cap of input, passes width of gates to gate cap function"""
|
||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||
|
||||
def get_intrinsic_capacitance(self):
|
||||
"""Get the drain capacitances of the TXs in the gate."""
|
||||
nmos_stack = 2
|
||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||
nmos_stack,
|
||||
self.tx_mults)
|
||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
1,
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,17 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import pgate
|
||||
from .pgate import *
|
||||
import debug
|
||||
from tech import drc, parameter, spice
|
||||
from vector import vector
|
||||
import logical_effort
|
||||
from base import vector
|
||||
from base import logical_effort
|
||||
from sram_factory import factory
|
||||
import contact
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class pnand3(pgate.pgate):
|
||||
class pnand3(pgate):
|
||||
"""
|
||||
This module generates gds of a parametrically sized 2-input nand.
|
||||
This model use ptx to generate a 2-input nand within a cetrain height.
|
||||
|
|
@ -82,7 +81,6 @@ class pnand3(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact="active",
|
||||
add_drain_contact="active")
|
||||
self.add_mod(self.nmos_center)
|
||||
|
||||
self.nmos_right = factory.create(module_type="ptx",
|
||||
width=self.nmos_width,
|
||||
|
|
@ -90,7 +88,6 @@ class pnand3(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact="active",
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.nmos_right)
|
||||
|
||||
self.nmos_left = factory.create(module_type="ptx",
|
||||
width=self.nmos_width,
|
||||
|
|
@ -98,7 +95,6 @@ class pnand3(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact="active")
|
||||
self.add_mod(self.nmos_left)
|
||||
|
||||
self.pmos_left = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -106,7 +102,6 @@ class pnand3(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_left)
|
||||
|
||||
self.pmos_center = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -114,7 +109,6 @@ class pnand3(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_center)
|
||||
|
||||
self.pmos_right = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -122,7 +116,6 @@ class pnand3(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_right)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Pre-compute some handy layout parameters. """
|
||||
|
|
@ -210,17 +203,17 @@ class pnand3(pgate.pgate):
|
|||
""" Route the A and B and C inputs """
|
||||
|
||||
# We can use this pitch because the contacts and overlap won't be adjacent
|
||||
non_contact_pitch = 0.5 * self.m1_width + self.m1_space + 0.5 * contact.poly_contact.second_layer_height
|
||||
non_contact_pitch = 0.5 * self.m1_width + self.m1_space + 0.5 * self.poly_contact.second_layer_height
|
||||
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
|
||||
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
|
||||
|
||||
bottom_pin = self.nmos1_inst.get_pin("D")
|
||||
# active contact metal to poly contact metal spacing
|
||||
active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height
|
||||
active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * self.poly_contact.second_layer_height
|
||||
# active diffusion to poly contact spacing
|
||||
# doesn't use nmos uy because that is calculated using offset + poly height
|
||||
active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * self.poly_contact.first_layer_height
|
||||
active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width
|
||||
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||
active_to_poly_contact,
|
||||
|
|
@ -334,12 +327,12 @@ class pnand3(pgate.pgate):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 3
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -350,27 +343,26 @@ class pnand3(pgate.pgate):
|
|||
|
||||
def is_non_inverting(self):
|
||||
"""Return input to output polarity for module"""
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_on_resistance(self):
|
||||
"""On resistance of pnand, defined by stacked NMOS"""
|
||||
is_nchannel = True
|
||||
stack = 3
|
||||
is_cell = False
|
||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||
|
||||
return self.tr_r_on(self.nmos_width, is_nchannel, stack, is_cell)
|
||||
|
||||
def get_input_capacitance(self):
|
||||
"""Input cap of input, passes width of gates to gate cap function"""
|
||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||
|
||||
return self.gate_c(self.nmos_width+self.pmos_width)
|
||||
|
||||
def get_intrinsic_capacitance(self):
|
||||
"""Get the drain capacitances of the TXs in the gate."""
|
||||
nmos_stack = 3
|
||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||
nmos_drain_c = self.drain_c_(self.nmos_width*self.tx_mults,
|
||||
nmos_stack,
|
||||
self.tx_mults)
|
||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
1,
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,17 +5,16 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import pgate
|
||||
from .pgate import *
|
||||
import debug
|
||||
from tech import drc, parameter, spice
|
||||
from vector import vector
|
||||
import logical_effort
|
||||
from base import vector
|
||||
from base import logical_effort
|
||||
from sram_factory import factory
|
||||
import contact
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class pnand4(pgate.pgate):
|
||||
class pnand4(pgate):
|
||||
"""
|
||||
This module generates gds of a parametrically sized 4-input nand.
|
||||
This model use ptx to generate a 4-input nand within a cetrain height.
|
||||
|
|
@ -82,7 +81,6 @@ class pnand4(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact="active",
|
||||
add_drain_contact="active")
|
||||
self.add_mod(self.nmos_center)
|
||||
|
||||
self.nmos_right = factory.create(module_type="ptx",
|
||||
width=self.nmos_width,
|
||||
|
|
@ -90,7 +88,6 @@ class pnand4(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact="active",
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.nmos_right)
|
||||
|
||||
self.nmos_left = factory.create(module_type="ptx",
|
||||
width=self.nmos_width,
|
||||
|
|
@ -98,7 +95,6 @@ class pnand4(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact="active")
|
||||
self.add_mod(self.nmos_left)
|
||||
|
||||
self.pmos_left = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -106,7 +102,6 @@ class pnand4(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_left)
|
||||
|
||||
self.pmos_center = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -114,7 +109,6 @@ class pnand4(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_center)
|
||||
|
||||
self.pmos_right = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -122,7 +116,6 @@ class pnand4(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_right)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Pre-compute some handy layout parameters. """
|
||||
|
|
@ -231,11 +224,11 @@ class pnand4(pgate.pgate):
|
|||
|
||||
bottom_pin = self.nmos1_inst.get_pin("D")
|
||||
# active contact metal to poly contact metal spacing
|
||||
active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height
|
||||
active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * self.poly_contact.second_layer_height
|
||||
# active diffusion to poly contact spacing
|
||||
# doesn't use nmos uy because that is calculated using offset + poly height
|
||||
active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height
|
||||
active_to_poly_contact = active_top + self.poly_to_active + 0.5 * self.poly_contact.first_layer_height
|
||||
active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width
|
||||
self.inputA_yoffset = max(active_contact_to_poly_contact,
|
||||
active_to_poly_contact,
|
||||
|
|
@ -356,12 +349,12 @@ class pnand4(pgate.pgate):
|
|||
Input inverted by this stage.
|
||||
"""
|
||||
parasitic_delay = 3
|
||||
return logical_effort.logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
return logical_effort(self.name,
|
||||
self.size,
|
||||
self.input_load(),
|
||||
cout,
|
||||
parasitic_delay,
|
||||
not inp_is_rise)
|
||||
|
||||
def build_graph(self, graph, inst_name, port_nets):
|
||||
"""
|
||||
|
|
@ -390,4 +383,4 @@ class pnand4(pgate.pgate):
|
|||
pmos_drain_c = self.drain_c_(self.pmos_width*self.tx_mults,
|
||||
1,
|
||||
self.tx_mults)
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
return nmos_drain_c + pmos_drain_c
|
||||
|
|
@ -5,15 +5,15 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import pgate
|
||||
from .pgate import *
|
||||
import debug
|
||||
from tech import drc, parameter, spice
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class pnor2(pgate.pgate):
|
||||
class pnor2(pgate):
|
||||
"""
|
||||
This module generates gds of a parametrically sized 2-input nor.
|
||||
This model use ptx to generate a 2-input nor within a cetrain height.
|
||||
|
|
@ -77,7 +77,6 @@ class pnor2(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.nmos_left)
|
||||
|
||||
self.nmos_right = factory.create(module_type="ptx",
|
||||
width=self.nmos_width,
|
||||
|
|
@ -85,7 +84,6 @@ class pnor2(pgate.pgate):
|
|||
tx_type="nmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.nmos_right)
|
||||
|
||||
self.pmos_left = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -93,7 +91,6 @@ class pnor2(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact=self.route_layer,
|
||||
add_drain_contact="active")
|
||||
self.add_mod(self.pmos_left)
|
||||
|
||||
self.pmos_right = factory.create(module_type="ptx",
|
||||
width=self.pmos_width,
|
||||
|
|
@ -101,7 +98,6 @@ class pnor2(pgate.pgate):
|
|||
tx_type="pmos",
|
||||
add_source_contact="active",
|
||||
add_drain_contact=self.route_layer)
|
||||
self.add_mod(self.pmos_right)
|
||||
|
||||
def setup_layout_constants(self):
|
||||
""" Pre-compute some handy layout parameters. """
|
||||
|
|
@ -5,15 +5,15 @@
|
|||
#
|
||||
from math import log, ceil
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from tech import layer, drc
|
||||
from globals import OPTS
|
||||
from tech import layer_properties as layer_props
|
||||
|
||||
|
||||
class port_address(design.design):
|
||||
class port_address(design):
|
||||
"""
|
||||
Create the address port (row decoder and wordline driver)..
|
||||
"""
|
||||
|
|
@ -76,16 +76,19 @@ class port_address(design.design):
|
|||
|
||||
def route_supplies(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]:
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
if layer_props.wordline_driver.vertical_supply:
|
||||
self.copy_layout_pin(self.rbl_driver_inst, "vdd")
|
||||
else:
|
||||
rbl_pos = self.rbl_driver_inst.get_pin("vdd").rc()
|
||||
self.add_power_pin("vdd", rbl_pos)
|
||||
self.add_path("m4", [rbl_pos, self.wordline_driver_array_inst.get_pins("vdd")[0].rc()])
|
||||
|
||||
for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"):
|
||||
if layer_props.port_address.supply_offset:
|
||||
self.copy_power_pin(rbl_vdd_pin)
|
||||
else:
|
||||
self.copy_power_pin(rbl_vdd_pin, loc=rbl_vdd_pin.lc())
|
||||
self.copy_layout_pin(self.wordline_driver_array_inst, "vdd")
|
||||
self.copy_layout_pin(self.wordline_driver_array_inst, "gnd")
|
||||
|
||||
self.copy_layout_pin(self.row_decoder_inst, "vdd")
|
||||
self.copy_layout_pin(self.row_decoder_inst, "gnd")
|
||||
|
||||
# Also connect the B input of the RBL and_dec to vdd
|
||||
if OPTS.local_array_size == 0:
|
||||
rbl_b_pin = self.rbl_driver_inst.get_pin("B")
|
||||
|
|
@ -145,12 +148,10 @@ class port_address(design.design):
|
|||
|
||||
self.row_decoder = factory.create(module_type="decoder",
|
||||
num_outputs=self.num_rows)
|
||||
self.add_mod(self.row_decoder)
|
||||
|
||||
self.wordline_driver_array = factory.create(module_type="wordline_driver_array",
|
||||
rows=self.num_rows,
|
||||
cols=self.num_cols)
|
||||
self.add_mod(self.wordline_driver_array)
|
||||
|
||||
local_array_size = OPTS.local_array_size
|
||||
if local_array_size > 0:
|
||||
|
|
@ -174,8 +175,6 @@ class port_address(design.design):
|
|||
size=driver_size,
|
||||
height=b.height)
|
||||
|
||||
self.add_mod(self.rbl_driver)
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the hierarchical row decoder """
|
||||
|
||||
|
|
@ -232,17 +231,15 @@ class port_address(design.design):
|
|||
wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0)
|
||||
self.wordline_driver_array_inst.place(wordline_driver_array_offset)
|
||||
|
||||
# The wordline driver also had an extra gap on the right, so use this offset
|
||||
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
||||
x_offset = self.wordline_driver_array_inst.rx() - well_gap - self.rbl_driver.width
|
||||
|
||||
# This m4_pitch corresponds to the offset space for jog routing in the
|
||||
# wordline_driver_array
|
||||
rbl_driver_offset = wordline_driver_array_offset + vector(2 * self.m4_pitch, 0)
|
||||
|
||||
if self.port == 0:
|
||||
rbl_driver_offset = vector(x_offset,
|
||||
0)
|
||||
self.rbl_driver_inst.place(rbl_driver_offset, "MX")
|
||||
else:
|
||||
rbl_driver_offset = vector(x_offset,
|
||||
self.wordline_driver_array.height)
|
||||
rbl_driver_offset += vector(0,
|
||||
self.wordline_driver_array.height)
|
||||
self.rbl_driver_inst.place(rbl_driver_offset)
|
||||
|
||||
# Pass this up
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@
|
|||
#
|
||||
from tech import drc
|
||||
import debug
|
||||
import design
|
||||
from base import design
|
||||
import math
|
||||
from sram_factory import factory
|
||||
from collections import namedtuple
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from tech import cell_properties
|
||||
from tech import layer_properties as layer_props
|
||||
|
||||
|
||||
class port_data(design.design):
|
||||
class port_data(design):
|
||||
"""
|
||||
Create the data port (column mux, sense amps, write driver, etc.) for the given port number.
|
||||
Port 0 always has the RBL on the left while port 1 is on the right.
|
||||
|
|
@ -29,7 +29,7 @@ class port_data(design.design):
|
|||
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
|
||||
else:
|
||||
self.num_wmasks = 0
|
||||
|
||||
|
||||
if num_spare_cols is not None:
|
||||
self.num_spare_cols = num_spare_cols + self.num_spare_cols
|
||||
if self.num_spare_cols is None:
|
||||
|
|
@ -215,7 +215,6 @@ class port_data(design.design):
|
|||
bitcell_bl=self.bl_names[self.port],
|
||||
bitcell_br=self.br_names[self.port],
|
||||
column_offset=self.port - 1)
|
||||
self.add_mod(self.precharge_array)
|
||||
|
||||
if self.port in self.read_ports:
|
||||
# RBLs don't get a sense amp
|
||||
|
|
@ -224,7 +223,6 @@ class port_data(design.design):
|
|||
offsets=self.bit_offsets,
|
||||
words_per_row=self.words_per_row,
|
||||
num_spare_cols=self.num_spare_cols)
|
||||
self.add_mod(self.sense_amp_array)
|
||||
else:
|
||||
self.sense_amp_array = None
|
||||
|
||||
|
|
@ -236,7 +234,6 @@ class port_data(design.design):
|
|||
offsets=self.bit_offsets,
|
||||
bitcell_bl=self.bl_names[self.port],
|
||||
bitcell_br=self.br_names[self.port])
|
||||
self.add_mod(self.column_mux_array)
|
||||
else:
|
||||
self.column_mux_array = None
|
||||
|
||||
|
|
@ -248,7 +245,6 @@ class port_data(design.design):
|
|||
offsets=self.bit_offsets,
|
||||
write_size=self.write_size,
|
||||
num_spare_cols=self.num_spare_cols)
|
||||
self.add_mod(self.write_driver_array)
|
||||
if self.write_size is not None:
|
||||
# RBLs don't get a write mask
|
||||
self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
|
||||
|
|
@ -256,7 +252,6 @@ class port_data(design.design):
|
|||
offsets=self.bit_offsets,
|
||||
word_size=self.word_size,
|
||||
write_size=self.write_size)
|
||||
self.add_mod(self.write_mask_and_array)
|
||||
else:
|
||||
self.write_mask_and_array = None
|
||||
|
||||
|
|
@ -516,9 +511,6 @@ class port_data(design.design):
|
|||
wdriver_inst = self.write_driver_array_inst
|
||||
|
||||
for bit in range(self.num_wmasks):
|
||||
# Bring write mask AND array output pin to port data level
|
||||
self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit))
|
||||
|
||||
wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit))
|
||||
wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit))
|
||||
|
||||
|
|
@ -858,10 +850,10 @@ class port_data(design.design):
|
|||
"""
|
||||
if self.column_mux_array:
|
||||
self.column_mux_array.graph_exclude_columns(column_include_num)
|
||||
|
||||
|
||||
def graph_clear_column_mux(self):
|
||||
"""
|
||||
Clear mux exclusions to allow different bit tests.
|
||||
"""
|
||||
if self.column_mux_array:
|
||||
self.column_mux_array.init_graph_params()
|
||||
self.column_mux_array.init_graph_params()
|
||||
|
|
|
|||
|
|
@ -5,18 +5,17 @@
|
|||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
import contact
|
||||
import design
|
||||
from base import design
|
||||
import debug
|
||||
from pgate import pgate
|
||||
from .pgate import *
|
||||
from tech import parameter, drc
|
||||
from vector import vector
|
||||
from base import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties as cell_props
|
||||
|
||||
|
||||
class precharge(design.design):
|
||||
class precharge(design):
|
||||
"""
|
||||
Creates a single precharge cell
|
||||
This module implements the precharge bitline cell used in the design.
|
||||
|
|
@ -71,7 +70,7 @@ class precharge(design.design):
|
|||
self.connect_poly()
|
||||
self.route_en()
|
||||
self.place_nwell_and_contact()
|
||||
self.route_vdd_rail()
|
||||
self.route_supplies()
|
||||
self.route_bitlines()
|
||||
self.connect_to_bitlines()
|
||||
self.add_boundary()
|
||||
|
|
@ -90,35 +89,19 @@ class precharge(design.design):
|
|||
width=self.ptx_width,
|
||||
mults=self.ptx_mults,
|
||||
tx_type="pmos")
|
||||
self.add_mod(self.pmos)
|
||||
|
||||
def route_vdd_rail(self):
|
||||
def route_supplies(self):
|
||||
"""
|
||||
Adds a vdd rail at the top of the cell
|
||||
"""
|
||||
|
||||
# Adds the rail across the width of the cell
|
||||
vdd_position = vector(0.5 * self.width, self.height)
|
||||
layer_width = drc("minwidth_" + self.en_layer)
|
||||
self.add_rect_center(layer=self.en_layer,
|
||||
offset=vdd_position,
|
||||
width=self.width,
|
||||
height=layer_width)
|
||||
|
||||
pmos_pin = self.upper_pmos2_inst.get_pin("S")
|
||||
pmos_pos = pmos_pin.center()
|
||||
self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos])
|
||||
|
||||
# center of vdd rail
|
||||
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
|
||||
self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos])
|
||||
|
||||
self.add_power_pin("vdd",
|
||||
self.well_contact_pos,
|
||||
directions=("V", "V"))
|
||||
|
||||
self.add_via_stack_center(from_layer=pmos_pin.layer,
|
||||
to_layer=self.en_layer,
|
||||
offset=pmos_pin.center(),
|
||||
directions=("V", "V"))
|
||||
self.add_layout_pin_rect_center(text="vdd",
|
||||
layer=pmos_pin.layer,
|
||||
offset=self.well_contact_pos)
|
||||
|
||||
def create_ptx(self):
|
||||
"""
|
||||
|
|
@ -194,7 +177,7 @@ class precharge(design.design):
|
|||
pin_offset = self.lower_pmos_inst.get_pin("G").lr()
|
||||
# This is an extra space down for some techs with contact to active spacing
|
||||
contact_space = max(self.poly_space,
|
||||
self.poly_contact_to_gate) + 0.5 * contact.poly_contact.first_layer_height
|
||||
self.poly_contact_to_gate) + 0.5 * self.poly_contact.first_layer_height
|
||||
offset = pin_offset - vector(0, contact_space)
|
||||
self.add_via_stack_center(from_layer="poly",
|
||||
to_layer=self.en_layer,
|
||||
|
|
@ -214,7 +197,7 @@ class precharge(design.design):
|
|||
|
||||
# adds the contact from active to metal1
|
||||
offset_height = self.upper_pmos1_inst.uy() + \
|
||||
contact.active_contact.height + \
|
||||
self.active_contact.height + \
|
||||
self.nwell_extend_active
|
||||
self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \
|
||||
vector(0, offset_height)
|
||||
|
|
@ -226,7 +209,7 @@ class precharge(design.design):
|
|||
to_layer=self.bitline_layer,
|
||||
offset=self.well_contact_pos)
|
||||
|
||||
self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space
|
||||
self.height = self.well_contact_pos.y + self.active_contact.height + self.m1_space
|
||||
|
||||
# nwell should span the whole design since it is pmos only
|
||||
self.add_rect(layer="nwell",
|
||||
|
|
@ -305,4 +288,3 @@ class precharge(design.design):
|
|||
self.add_path(self.bitline_layer,
|
||||
[left_pos, right_pos],
|
||||
width=pmos_pin.height())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue