mirror of https://github.com/VLSIDA/OpenRAM.git
merge dev into datasheet_gen; fixed merge conflict in hierarchy_design.py
This commit is contained in:
commit
59c0421804
|
|
@ -7,6 +7,26 @@ list at openram-dev-group@ucsc.edu. We are happy to give insights into
|
||||||
the best way to implement a change to ensure your contribution will be
|
the best way to implement a change to ensure your contribution will be
|
||||||
accepted and help other OpenRAM users.
|
accepted and help other OpenRAM users.
|
||||||
|
|
||||||
|
# Directory Structure
|
||||||
|
|
||||||
|
* compiler - openram compiler itself (pointed to by OPENRAM_HOME)
|
||||||
|
* compiler/base - base data structure modules
|
||||||
|
* compiler/pgates - parameterized cells (e.g. logic gates)
|
||||||
|
* compiler/bitcells - various bitcell styles
|
||||||
|
* compiler/modules - high-level modules (e.g. decoders, etc.)
|
||||||
|
* compiler/verify - DRC and LVS verification wrappers
|
||||||
|
* compiler/characterizer - timing characterization code
|
||||||
|
* compiler/gdsMill - GDSII reader/writer
|
||||||
|
* compiler/router - router for signals and power supplies
|
||||||
|
* compiler/tests - unit tests
|
||||||
|
* technology - openram technology directory (pointed to by OPENRAM_TECH)
|
||||||
|
* technology/freepdk45 - example configuration library for [FreePDK45 technology node
|
||||||
|
* technology/scn4m_subm - example configuration library [SCMOS] technology node
|
||||||
|
* technology/scn3me_subm - unsupported configuration (not enough metal layers)
|
||||||
|
* technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies
|
||||||
|
* docs - LaTeX manual (outdated)
|
||||||
|
* lib - IP library of pregenerated memories
|
||||||
|
|
||||||
# Code Style
|
# Code Style
|
||||||
|
|
||||||
Our code may not be the best and we acknowledge that. We welcome
|
Our code may not be the best and we acknowledge that. We welcome
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
# Debugging
|
||||||
|
|
||||||
|
When OpenRAM runs, it puts files in a temporary directory that is
|
||||||
|
shown in the banner at the top. Like:
|
||||||
|
```
|
||||||
|
/tmp/openram_mrg_18128_temp/
|
||||||
|
```
|
||||||
|
This is where simulations and DRC/LVS get run so there is no network
|
||||||
|
traffic. The directory name is unique for each person and run of
|
||||||
|
OpenRAM to not clobber any files and allow simultaneous runs. If it
|
||||||
|
passes, the files are deleted. If it fails, you will see these files:
|
||||||
|
+ temp.gds is the layout (.mag files too if using SCMOS)
|
||||||
|
+ temp.sp is the netlist
|
||||||
|
+ test1.drc.err is the std err output of the DRC command
|
||||||
|
+ test1.drc.out is the standard output of the DRC command
|
||||||
|
+ test1.drc.results is the DRC results file
|
||||||
|
+ test1.lvs.err is the std err output of the LVS command
|
||||||
|
+ test1.lvs.out is the standard output of the LVS command
|
||||||
|
+ test1.lvs.results is the DRC results file
|
||||||
|
|
||||||
|
Depending on your DRC/LVS tools, there will also be:
|
||||||
|
+ \_calibreDRC.rul\_ is the DRC rule file (Calibre)
|
||||||
|
+ dc_runset is the command file (Calibre)
|
||||||
|
+ extracted.sp (Calibre)
|
||||||
|
+ run_lvs.sh is a Netgen script for LVS (Netgen)
|
||||||
|
+ run_drc.sh is a Magic script for DRC (Magic)
|
||||||
|
+ <topcell>.spice (Magic)
|
||||||
|
|
||||||
|
If DRC/LVS fails, the first thing is to check if it ran in the .out and
|
||||||
|
.err file. This shows the standard output and error output from
|
||||||
|
running DRC/LVS. If there is a setup problem it will be shown here.
|
||||||
|
|
||||||
|
If DRC/LVS runs, but doesn't pass, you then should look at the .results
|
||||||
|
file. If the DRC fails, it will typically show you the command that was used
|
||||||
|
to run Calibre or Magic+Netgen.
|
||||||
|
|
||||||
|
To debug, you will need a layout viewer. I prefer to use Glade
|
||||||
|
on my Mac, but you can also use Calibre, Magic, etc.
|
||||||
|
|
||||||
|
1. Calibre
|
||||||
|
|
||||||
|
Start the Calibre DESIGNrev viewer in the temp directory and load your GDS file:
|
||||||
|
```
|
||||||
|
calibredrv temp.gds
|
||||||
|
```
|
||||||
|
Select Verification->Start RVE and select the results database file in
|
||||||
|
the new form (e.g., test1.drc.db). This will start the RVE (results
|
||||||
|
viewer). Scroll through the check pane and find the DRC check with an
|
||||||
|
error. Select it and it will open some numbers to the right. Double
|
||||||
|
click on any of the errors in the result browser. These will be
|
||||||
|
labelled as numbers "1 2 3 4" for example will be 4 DRC errors.
|
||||||
|
|
||||||
|
In the viewer ">" opens the layout down a level.
|
||||||
|
|
||||||
|
2. Glade
|
||||||
|
|
||||||
|
You can view errors in Glade as well. I like this because it is on my laptop.
|
||||||
|
You can get it from: http://www.peardrop.co.uk/glade/
|
||||||
|
|
||||||
|
To remote display over X windows, you need to disable OpenGL acceleration or use vnc
|
||||||
|
or something. You can disable by adding this to your .bashrc in bash:
|
||||||
|
```
|
||||||
|
export GLADE_USE_OPENGL=no
|
||||||
|
```
|
||||||
|
or in .cshrc/.tcshrc in csh/tcsh:
|
||||||
|
```
|
||||||
|
setenv GLADE_USE_OPENGAL no
|
||||||
|
```
|
||||||
|
To use this with the FreePDK45 or SCMOS layer views you should use the
|
||||||
|
tech files. Then create a .glade.py file in your user directory with
|
||||||
|
these commands to load the technology layers:
|
||||||
|
```
|
||||||
|
ui().importCds("default",
|
||||||
|
"/Users/mrg/techfiles/freepdk45/display.drf",
|
||||||
|
"/Users/mrg/techfiles/freepdk45/FreePDK45.tf", 1000, 1,
|
||||||
|
"/Users/mrg/techfiles/freepdk45/layers.map")
|
||||||
|
```
|
||||||
|
Obviously, edit the paths to point to your directory. To switch
|
||||||
|
between processes, you have to change the importCds command (or you
|
||||||
|
can manually run the command each time you start glade).
|
||||||
|
|
||||||
|
To load the errors, you simply do Verify->Import Calibre Errors select
|
||||||
|
the .results file from Calibre.
|
||||||
|
|
||||||
|
3. Magic
|
||||||
|
|
||||||
|
Magic is only supported in SCMOS. You will need to install the MOSIS SCMOS rules
|
||||||
|
and Magic from: http://opencircuitdesign.com/
|
||||||
|
|
||||||
|
When running DRC or extraction, OpenRAM will load the GDS file, save
|
||||||
|
the .ext/.mag files, and export an extracted netlist (.spice).
|
||||||
|
|
||||||
|
4. It is possible to use other viewers as well, such as:
|
||||||
|
* LayoutEditor http://www.layouteditor.net/
|
||||||
|
|
||||||
|
|
||||||
|
# Example to output/input .gds layout files from/to Cadence
|
||||||
|
|
||||||
|
1. To create your component layouts, you should stream them to
|
||||||
|
individual gds files using our provided layermap and flatten
|
||||||
|
cells. For example,
|
||||||
|
```
|
||||||
|
strmout -layerMap layers.map -library sram -topCell $i -view layout -flattenVias -flattenPcells -strmFile ../gds_lib/$i.gds
|
||||||
|
```
|
||||||
|
2. To stream a layout back into Cadence, do this:
|
||||||
|
```
|
||||||
|
strmin -layerMap layers.map -attachTechFileOfLib NCSU\_TechLib\_FreePDK45 -library sram_4_32 -strmFile sram_4_32.gds
|
||||||
|
```
|
||||||
|
When you import a gds file, make sure to attach the correct tech lib
|
||||||
|
or you will get incorrect layers in the resulting library.
|
||||||
336
README.md
336
README.md
|
|
@ -1,238 +1,214 @@
|
||||||
# BASIC SETUP
|
# OpenRAM
|
||||||
|
[](https://github.com/VLSIDA/PrivateRAM/commits)
|
||||||
|
[](https://github.com/VLSIDA/PrivateRAM/archive/dev.zip)
|
||||||
|
[](./LICENSE)
|
||||||
|
|
||||||
Please look at the OpenRAM ICCAD paper and presentation in the repository:
|
An open-source static random access memory (SRAM) compiler.
|
||||||
https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_paper.pdf
|
|
||||||
https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_presentation.pdf
|
# What is OpenRAM?
|
||||||
|
|
||||||
|
<img align="right" width="25%" src="images/SCMOS_16kb_sram.jpg">
|
||||||
|
|
||||||
|
OpenRAM is an open-source Python framework to create the layout,
|
||||||
|
netlists, timing and power models, placement and routing models, and
|
||||||
|
other views necessary to use SRAMs in ASIC design. OpenRAM supports
|
||||||
|
integration in both commercial and open-source flows with both
|
||||||
|
predictive and fabricable technologies.
|
||||||
|
|
||||||
|
# Basic Setup
|
||||||
|
|
||||||
The OpenRAM compiler has very few 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)
|
+ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
|
||||||
* Python 3.5 and higher
|
+ Python 3.5 or higher
|
||||||
* Python numpy (pip3 install numpy to install)
|
+ Python numpy (pip3 install numpy to install)
|
||||||
* flask_table (pip3 install flask to install)
|
+ flask_table (pip3 install flask to install)
|
||||||
* a setup script for each technology you want to use
|
|
||||||
* a technology directory for each technology with the base cells
|
|
||||||
|
|
||||||
If you want to perform DRC and LVS, you will need either:
|
If you want to perform DRC and LVS, you will need either:
|
||||||
* Calibre (for FreePDK45 or SCMOS)
|
+ Calibre (for [FreePDK45])
|
||||||
* Magic + Netgen (for SCMOS only)
|
+ [Magic] + [Netgen] (for [SCMOS])
|
||||||
|
|
||||||
|
You must set two environment variables:
|
||||||
|
+ OPENRAM\_HOME should point to the compiler source directory.
|
||||||
|
+ OPENERAM\_TECH should point to a root technology directory.
|
||||||
|
|
||||||
|
For example add this to your .bashrc:
|
||||||
|
|
||||||
You must set two environment variables: OPENRAM_HOME should point to
|
|
||||||
the compiler source directory. OPENERAM_TECH should point to a root
|
|
||||||
technology directory that contains subdirs of all other technologies.
|
|
||||||
For example, in bash, add to your .bashrc:
|
|
||||||
```
|
```
|
||||||
export OPENRAM_HOME="$HOME/openram/compiler"
|
export OPENRAM_HOME="$HOME/openram/compiler"
|
||||||
export OPENRAM_TECH="$HOME/openram/technology"
|
export OPENRAM_TECH="$HOME/openram/technology"
|
||||||
```
|
```
|
||||||
For example, in csh/tcsh, add to your .cshrc/.tcshrc:
|
|
||||||
```
|
|
||||||
setenv OPENRAM_HOME "$HOME/openram/compiler"
|
|
||||||
setenv OPENRAM_TECH "$HOME/openram/technology"
|
|
||||||
```
|
|
||||||
|
|
||||||
We include the tech files necessary for FreePDK and SCMOS. The SCMOS
|
We include the tech files necessary for [FreePDK45] and [SCMOS]
|
||||||
spice models, however, are generic and should be replaced with foundry
|
SCN4M_SUBM. The [SCMOS] spice models, however, are generic and should
|
||||||
models.
|
be replaced with foundry models. If you are using [FreePDK45], you
|
||||||
If you are using FreePDK, you should also have that set up and have the
|
should also have that set up and have the environment variable point
|
||||||
environment variable point to the PDK.
|
to the PDK. For example add this to your .bashrc:
|
||||||
For example, in bash, add to your .bashrc:
|
|
||||||
```
|
```
|
||||||
export FREEPDK45="/bsoe/software/design-kits/FreePDK45"
|
export FREEPDK45="/bsoe/software/design-kits/FreePDK45"
|
||||||
```
|
```
|
||||||
For example, in csh/tcsh, add to your .tcshrc:
|
|
||||||
|
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].
|
||||||
|
|
||||||
|
# Basic Usage
|
||||||
|
|
||||||
|
Once you have defined the environment, you can run OpenRAM from the command line
|
||||||
|
using a single configuration file written in Python. You may wish to add
|
||||||
|
$OPENRAM\_HOME to your $PYTHONPATH.
|
||||||
|
|
||||||
|
For example, create a file called *myconfig.py* specifying the following
|
||||||
|
parameters for your memory:
|
||||||
|
|
||||||
```
|
```
|
||||||
setenv FREEPDK45 "/bsoe/software/design-kits/FreePDK45"
|
# Data word size
|
||||||
|
word_size = 2
|
||||||
|
# Number of words in the memory
|
||||||
|
num_words = 16
|
||||||
|
|
||||||
|
# Technology to use in $OPENRAM\_TECH
|
||||||
|
tech_name = "scn4m_subm"
|
||||||
|
# Process corners to characterize
|
||||||
|
process_corners = ["TT"]
|
||||||
|
# Voltage corners to characterize
|
||||||
|
supply_voltages = [ 3.3 ]
|
||||||
|
# Temperature corners to characterize
|
||||||
|
temperatures = [ 25 ]
|
||||||
|
|
||||||
|
# Output directory for the results
|
||||||
|
output_path = "temp"
|
||||||
|
# Output file base name
|
||||||
|
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
|
||||||
|
|
||||||
|
# Disable analytical models for full characterization (WARNING: slow!)
|
||||||
|
# analytical_delay = False
|
||||||
```
|
```
|
||||||
We do not distribute the PDK, but you may get it from:
|
|
||||||
https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
|
|
||||||
|
|
||||||
If you are using SCMOS, you should install Magic and netgen from:
|
You can then run OpenRAM by executing:
|
||||||
http://opencircuitdesign.com/magic/
|
```
|
||||||
http://opencircuitdesign.com/netgen/
|
python3 $OPENRAM\_HOME/openram.py myconfig
|
||||||
We have included the SCN4M design rules from QFlow:
|
```
|
||||||
http://opencircuitdesign.com/qflow/
|
You can see all of the options for the configuration file in
|
||||||
|
$OPENRAM\_HOME/options.py
|
||||||
# DIRECTORY STRUCTURE
|
|
||||||
|
|
||||||
* compiler - openram compiler itself (pointed to by OPENRAM_HOME)
|
|
||||||
* compiler/base - base data structure modules
|
|
||||||
* compiler/pgates - parameterized cells (e.g. logic gates)
|
|
||||||
* compiler/bitcells - various bitcell styles
|
|
||||||
* compiler/modules - high-level modules (e.g. decoders, etc.)
|
|
||||||
* compiler/verify - DRC and LVS verification wrappers
|
|
||||||
* compiler/characterizer - timing characterization code
|
|
||||||
* compiler/gdsMill - GDSII reader/writer
|
|
||||||
* compiler/router - router for signals and power supplies
|
|
||||||
* compiler/tests - unit tests
|
|
||||||
* technology - openram technology directory (pointed to by OPENRAM_TECH)
|
|
||||||
* technology/freepdk45 - example configuration library for freepdk45 technology node
|
|
||||||
* technology/scn4m_subm - example configuration library SCMOS technology node
|
|
||||||
* technology/scn3me_subm - unsupported configuration (not enough metal layers)
|
|
||||||
* technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies
|
|
||||||
* docs - LaTeX manual (outdated)
|
|
||||||
* lib - IP library of pregenerated memories
|
|
||||||
|
|
||||||
|
|
||||||
# UNIT TESTS
|
# Unit Tests
|
||||||
|
|
||||||
Regression testing performs a number of tests for all modules in OpenRAM.
|
Regression testing performs a number of tests for all modules in OpenRAM.
|
||||||
|
From the unit test directory ($OPENRAM\_HOME/tests),
|
||||||
|
use the following command to run all regression tests:
|
||||||
|
|
||||||
Use the command:
|
|
||||||
```
|
```
|
||||||
python regress.py
|
python3 regress.py
|
||||||
```
|
```
|
||||||
To run a specific test:
|
To run a specific test:
|
||||||
```
|
```
|
||||||
python {unit test}.py
|
python3 {unit test}.py
|
||||||
```
|
```
|
||||||
The unit tests take the same arguments as openram.py itself.
|
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 increase the verbosity of the test, add one (or more) -v options:
|
||||||
```
|
```
|
||||||
python tests/00_code_format_check_test.py -v -t freepdk45
|
python3 tests/00_code_format_check_test.py -v -t freepdk45
|
||||||
```
|
```
|
||||||
To specify a particular technology use "-t <techname>" such as
|
To specify a particular technology use "-t <techname>" such as
|
||||||
"-t freepdk45" or "-t scn4m_subm". The default for a unit test is scn4m_subm.
|
"-t freepdk45" or "-t scn4m\_subm". The default for a unit test is scn4m_subm.
|
||||||
The default for openram.py is specified in the configuration file.
|
The default for openram.py is specified in the configuration file.
|
||||||
|
|
||||||
|
|
||||||
# CREATING CUSTOM TECHNOLOGIES
|
# Porting to a New Technology
|
||||||
|
|
||||||
All setup scripts should be in the setup_scripts directory under the
|
If you want to support a enw technology, you will need to create:
|
||||||
$OPENRAM_TECH directory. Please look at the following file for an
|
+ a setup script for each technology you want to use
|
||||||
|
+ a technology directory for each technology with the base cells
|
||||||
|
|
||||||
|
All setup scripts should be in the setup\_scripts directory under the
|
||||||
|
$OPENRAM\_TECH directory. We provide two technology examples for
|
||||||
|
[SCMOS] and [FreePDK45]. Please look at the following file for an
|
||||||
example of what is needed for OpenRAM:
|
example of what is needed for OpenRAM:
|
||||||
|
|
||||||
```
|
```
|
||||||
$OPENRAM_TECH/setup_scripts/setup_openram_freepdk45.py
|
$OPENRAM_TECH/setup_scripts/setup_openram_freepdk45.py
|
||||||
```
|
```
|
||||||
Each setup script should be named as: setup_openram_{tech name}.py.
|
|
||||||
|
|
||||||
Each specific technology (e.g., freepdk45) should be a subdirectory
|
Each setup script should be named as: setup\_openram\_{tech name}.py.
|
||||||
|
|
||||||
|
Each specific technology (e.g., [FreePDK45]) should be a subdirectory
|
||||||
(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files:
|
(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files:
|
||||||
1. gds_lib folder with all the .gds (premade) library cells. At a
|
* gds_lib folder with all the .gds (premade) library cells:
|
||||||
minimum this includes:
|
* dff.gds
|
||||||
* ms_flop.gds
|
* sense_amp.gds
|
||||||
* sense_amp.gds
|
* write_driver.gds
|
||||||
* write_driver.gds
|
* cell_6t.gds
|
||||||
* cell_6t.gds
|
* replica\_cell\_6t.gds
|
||||||
* replica_cell_6t.gds
|
* sp_lib folder with all the .sp (premade) library netlists for the above cells.
|
||||||
* tri_gate.gds
|
* layers.map
|
||||||
2. sp_lib folder with all the .sp (premade) library netlists for the above cells.
|
* A valid tech Python module (tech directory with __init__.py and tech.py) with:
|
||||||
3. layers.map
|
* References in tech.py to spice models
|
||||||
4. A valid tech Python module (tech directory with __init__.py and tech.py) with:
|
* DRC/LVS rules needed for dynamic cells and routing
|
||||||
* References in tech.py to spice models
|
* Layer information
|
||||||
* DRC/LVS rules needed for dynamic cells and routing
|
* Spice and supply information
|
||||||
* Layer information
|
* etc.
|
||||||
* etc.
|
|
||||||
|
|
||||||
# DEBUGGING
|
# Get Involved
|
||||||
|
|
||||||
When OpenRAM runs, it puts files in a temporary directory that is
|
+ Report bugs by submitting [Github issues].
|
||||||
shown in the banner at the top. Like:
|
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
|
||||||
```
|
+ Submit code/fixes using a [Github pull request]
|
||||||
/tmp/openram_mrg_18128_temp/
|
+ Follow our [project][Github projects].
|
||||||
```
|
+ Read and cite our [ICCAD paper][OpenRAMpaper]
|
||||||
This is where simulations and DRC/LVS get run so there is no network
|
|
||||||
traffic. The directory name is unique for each person and run of
|
|
||||||
OpenRAM to not clobber any files and allow simultaneous runs. If it
|
|
||||||
passes, the files are deleted. If it fails, you will see these files:
|
|
||||||
* temp.gds is the layout
|
|
||||||
* (.mag files if using SCMOS)
|
|
||||||
* temp.sp is the netlist
|
|
||||||
* test1.drc.err is the std err output of the DRC command
|
|
||||||
* test1.drc.out is the standard output of the DRC command
|
|
||||||
* test1.drc.results is the DRC results file
|
|
||||||
* test1.lvs.err is the std err output of the LVS command
|
|
||||||
* test1.lvs.out is the standard output of the LVS command
|
|
||||||
* test1.lvs.results is the DRC results file
|
|
||||||
|
|
||||||
Depending on your DRC/LVS tools, there will also be:
|
# Further Help
|
||||||
* _calibreDRC.rul_ is the DRC rule file (Calibre)
|
|
||||||
* dc_runset is the command file (Calibre)
|
|
||||||
* extracted.sp (Calibre)
|
|
||||||
* run_lvs.sh is a Netgen script for LVS (Netgen)
|
|
||||||
* run_drc.sh is a Magic script for DRC (Magic)
|
|
||||||
* <topcell>.spice (Magic)
|
|
||||||
|
|
||||||
If DRC/LVS fails, the first thing is to check if it ran in the .out and
|
+ [Additional hints](./HINTS.md)
|
||||||
.err file. This shows the standard output and error output from
|
+ [OpenRAM Slack Workspace][Slack]
|
||||||
running DRC/LVS. If there is a setup problem it will be shown here.
|
+ [OpenRAM Users Group][user-group] ([subscribe here][user-group-subscribe])
|
||||||
|
+ [OpenRAM Developers Group][dev-group] ([subscribe here][dev-group-subscribe])
|
||||||
|
|
||||||
If DRC/LVS runs, but doesn't pass, you then should look at the .results
|
# License
|
||||||
file. If the DRC fails, it will typically show you the command that was used
|
|
||||||
to run Calibre or Magic+Netgen.
|
|
||||||
|
|
||||||
To debug, you will need a layout viewer. I prefer to use Glade
|
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
|
||||||
on my Mac, but you can also use Calibre, Magic, etc.
|
|
||||||
|
|
||||||
1. Calibre
|
# Contributors & Acknowledgment
|
||||||
|
|
||||||
Start the Calibre DESIGNrev viewer in the temp directory and load your GDS file:
|
- [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
|
||||||
```
|
- [James Stine] from [VLSIARCH] co-founded the project.
|
||||||
calibredrv temp.gds
|
- Hunter Nichols maintains and updates the timing characterization.
|
||||||
```
|
- Michael Grims created and maintains the multiport netlist code.
|
||||||
Select Verification->Start RVE and select the results database file in
|
- Jennifer Sowash is creating the OpenRAM IP library.
|
||||||
the new form (e.g., test1.drc.db). This will start the RVE (results
|
- Jesse Cirimelli-Low created the datasheet generation.
|
||||||
viewer). Scroll through the check pane and find the DRC check with an
|
- Samira Ataei created early multi-bank layouts and control logic.
|
||||||
error. Select it and it will open some numbers to the right. Double
|
- Bin Wu created early parameterized cells.
|
||||||
click on any of the errors in the result browser. These will be
|
- Yusu Wang is porting parameterized cells to new technologies.
|
||||||
labelled as numbers "1 2 3 4" for example will be 4 DRC errors.
|
- Brian Chen created early prototypes of the timing characterizer.
|
||||||
|
- Jeff Butera created early prototypes of the bank layout.
|
||||||
|
|
||||||
In the viewer ">" opens the layout down a level.
|
* * *
|
||||||
|
|
||||||
2. Glade
|
[Matthew Guthaus]: https://users.soe.ucsc.edu/~mrg
|
||||||
|
[James Stine]: https://ece.okstate.edu/content/stine-james-e-jr-phd
|
||||||
|
[VLSIDA]: https://vlsida.soe.ucsc.edu
|
||||||
|
[VLSIARCH]: https://vlsiarch.ecen.okstate.edu/
|
||||||
|
[OpenRAMpaper]: https://ieeexplore.ieee.org/document/7827670/
|
||||||
|
|
||||||
You can view errors in Glade as well. I like this because it is on my laptop.
|
[Github issues]: https://github.com/PrivateRAM/PrivateRAM/issues
|
||||||
You can get it from: http://www.peardrop.co.uk/glade/
|
[Github pull request]: https://github.com/PrivateRAM/PrivateRAM/pulls
|
||||||
|
[Github projects]: https://github.com/PrivateRAM/PrivateRAM/projects
|
||||||
|
|
||||||
To remote display over X windows, you need to disable OpenGL acceleration or use vnc
|
[email me]: mailto:mrg+openram@ucsc.edu
|
||||||
or something. You can disable by adding this to your .bashrc in bash:
|
[dev-group]: mailto:openram-dev-group@ucsc.edu
|
||||||
```
|
[user-group]: mailto:openram-user-group@ucsc.edu
|
||||||
export GLADE_USE_OPENGL=no
|
[dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu
|
||||||
```
|
[user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu
|
||||||
or in .cshrc/.tcshrc in csh/tcsh:
|
|
||||||
```
|
|
||||||
setenv GLADE_USE_OPENGAL no
|
|
||||||
```
|
|
||||||
To use this with the FreePDK45 or SCMOS layer views you should use the
|
|
||||||
tech files. Then create a .glade.py file in your user directory with
|
|
||||||
these commands to load the technology layers:
|
|
||||||
```
|
|
||||||
ui().importCds("default",
|
|
||||||
"/Users/mrg/techfiles/freepdk45/display.drf",
|
|
||||||
"/Users/mrg/techfiles/freepdk45/FreePDK45.tf", 1000, 1,
|
|
||||||
"/Users/mrg/techfiles/freepdk45/layers.map")
|
|
||||||
```
|
|
||||||
Obviously, edit the paths to point to your directory. To switch
|
|
||||||
between processes, you have to change the importCds command (or you
|
|
||||||
can manually run the command each time you start glade).
|
|
||||||
|
|
||||||
To load the errors, you simply do Verify->Import Calibre Errors select
|
[Magic]: http://opencircuitdesign.com/magic/
|
||||||
the .results file from Calibre.
|
[Netgen]: http://opencircuitdesign.com/netgen/
|
||||||
|
[Qflow]: http://opencircuitdesign.com/qflow/history.html
|
||||||
|
[Ngspice]: http://ngspice.sourceforge.net/
|
||||||
|
|
||||||
3. Magic
|
[OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/
|
||||||
|
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
|
||||||
Magic is only supported in SCMOS. You will need to install the MOSIS SCMOS rules
|
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
|
||||||
and Magic from: http://opencircuitdesign.com/
|
|
||||||
|
|
||||||
When running DRC or extraction, OpenRAM will load the GDS file, save
|
|
||||||
the .ext/.mag files, and export an extracted netlist (.spice).
|
|
||||||
|
|
||||||
4. It is possible to use other viewers as well, such as:
|
|
||||||
* LayoutEditor http://www.layouteditor.net/
|
|
||||||
|
|
||||||
|
|
||||||
# Example to output/input .gds layout files from/to Cadence
|
|
||||||
|
|
||||||
1. To create your component layouts, you should stream them to
|
|
||||||
individual gds files using our provided layermap and flatten
|
|
||||||
cells. For example,
|
|
||||||
```
|
|
||||||
strmout -layerMap layers.map -library sram -topCell $i -view layout -flattenVias -flattenPcells -strmFile ../gds_lib/$i.gds
|
|
||||||
```
|
|
||||||
2. To stream a layout back into Cadence, do this:
|
|
||||||
```
|
|
||||||
strmin -layerMap layers.map -attachTechFileOfLib NCSU_TechLib_FreePDK45 -library sram_4_32 -strmFile sram_4_32.gds
|
|
||||||
```
|
|
||||||
When you import a gds file, make sure to attach the correct tech lib
|
|
||||||
or you will get incorrect layers in the resulting library.
|
|
||||||
|
|
||||||
|
[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LTE4ODMyM2I0Mzk2ZmFiMjgwYTYyMTQ4NTgwMmUwMDhiM2E1MDViNDRjYzU1NjJhZTQxNWZjMzE3M2FlODBmZjA
|
||||||
|
|
|
||||||
|
|
@ -70,40 +70,47 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
"""Checks both DRC and LVS for a module"""
|
"""Checks both DRC and LVS for a module"""
|
||||||
# Unit tests will check themselves.
|
# Unit tests will check themselves.
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
|
|
||||||
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
|
|
||||||
global total_drc_errors
|
global total_drc_errors
|
||||||
global total_lvs_errors
|
global total_lvs_errors
|
||||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||||
self.sp_write(tempspice)
|
self.sp_write(tempspice)
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
num_drc_errors = verify.run_drc(self.name, tempgds)
|
|
||||||
num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification)
|
num_drc_errors = verify.run_drc(self.name, tempgds, final_verification)
|
||||||
|
num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification)
|
||||||
debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors))
|
debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors))
|
||||||
debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors))
|
debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors))
|
||||||
total_drc_errors += num_drc_errors
|
total_drc_errors += num_drc_errors
|
||||||
total_lvs_errors += num_lvs_errors
|
total_lvs_errors += num_lvs_errors
|
||||||
|
|
||||||
os.remove(tempspice)
|
os.remove(tempspice)
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def DRC(self):
|
def DRC(self, final_verification=False):
|
||||||
"""Checks DRC for a module"""
|
"""Checks DRC for a module"""
|
||||||
# Unit tests will check themselves.
|
# Unit tests will check themselves.
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
|
|
||||||
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
global total_drc_errors
|
global total_drc_errors
|
||||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||||
self.gds_write(tempgds)
|
self.gds_write(tempgds)
|
||||||
num_errors = verify.run_drc(self.name, tempgds)
|
num_errors = verify.run_drc(self.name, tempgds, final_verification)
|
||||||
total_drc_errors += num_errors
|
total_drc_errors += num_errors
|
||||||
debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error))
|
debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error))
|
||||||
|
|
||||||
os.remove(tempgds)
|
os.remove(tempgds)
|
||||||
|
|
||||||
def LVS(self, final_verification=False):
|
def LVS(self, final_verification=False):
|
||||||
"""Checks LVS for a module"""
|
"""Checks LVS for a module"""
|
||||||
# Unit tests will check themselves.
|
# Unit tests will check themselves.
|
||||||
# Do not run if disabled in options.
|
# Do not run if disabled in options.
|
||||||
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
|
|
||||||
|
if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)):
|
||||||
global total_lvs_errors
|
global total_lvs_errors
|
||||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,10 @@ def parse_args():
|
||||||
help="Base output file name(s) prefix", metavar="FILE"),
|
help="Base output file name(s) prefix", metavar="FILE"),
|
||||||
optparse.make_option("-p", "--outpath", dest="output_path",
|
optparse.make_option("-p", "--outpath", dest="output_path",
|
||||||
help="Output file(s) location"),
|
help="Output file(s) location"),
|
||||||
|
optparse.make_option("-i", "--inlinecheck", action="store_true",
|
||||||
|
help="Enable inline LVS/DRC checks", dest="inline_lvsdrc"),
|
||||||
optparse.make_option("-n", "--nocheck", action="store_false",
|
optparse.make_option("-n", "--nocheck", action="store_false",
|
||||||
help="Disable inline LVS/DRC checks", dest="check_lvsdrc"),
|
help="Disable all LVS/DRC checks", dest="check_lvsdrc"),
|
||||||
optparse.make_option("-v", "--verbose", action="count", dest="debug_level",
|
optparse.make_option("-v", "--verbose", action="count", dest="debug_level",
|
||||||
help="Increase the verbosity level"),
|
help="Increase the verbosity level"),
|
||||||
optparse.make_option("-t", "--tech", dest="tech_name",
|
optparse.make_option("-t", "--tech", dest="tech_name",
|
||||||
|
|
@ -388,10 +390,10 @@ def import_tech():
|
||||||
def print_time(name, now_time, last_time=None):
|
def print_time(name, now_time, last_time=None):
|
||||||
""" Print a statement about the time delta. """
|
""" Print a statement about the time delta. """
|
||||||
if last_time:
|
if last_time:
|
||||||
time = round((now_time-last_time).total_seconds(),1)
|
time = str(round((now_time-last_time).total_seconds(),1)) + " seconds"
|
||||||
else:
|
else:
|
||||||
time = now_time
|
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
|
||||||
print("** {0}: {1} seconds".format(name,time))
|
print("** {0}: {1}".format(name,time))
|
||||||
|
|
||||||
|
|
||||||
def report_status():
|
def report_status():
|
||||||
|
|
@ -414,6 +416,9 @@ def report_status():
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
print("Netlist only mode (no physical design is being done).")
|
print("Netlist only mode (no physical design is being done).")
|
||||||
|
|
||||||
|
if not OPTS.inline_lvsdrc:
|
||||||
|
print("DRC/LVS/PEX is only run on the top-level design.")
|
||||||
|
|
||||||
if not OPTS.check_lvsdrc:
|
if not OPTS.check_lvsdrc:
|
||||||
print("DRC/LVS/PEX checking is disabled.")
|
print("DRC/LVS/PEX is completely disabled.")
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -27,12 +27,12 @@ class bank_select(design.design):
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.calculate_module_offsets()
|
self.calculate_module_offsets()
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route_modules()
|
self.route_instances()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ class bank_select(design.design):
|
||||||
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
|
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
|
||||||
self.width = self.xoffset_inv + self.inv4x.width
|
self.width = self.xoffset_inv + self.inv4x.width
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
|
|
||||||
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
|
||||||
mod=self.inv_sel)
|
mod=self.inv_sel)
|
||||||
|
|
@ -152,7 +152,7 @@ class bank_select(design.design):
|
||||||
"vdd",
|
"vdd",
|
||||||
"gnd"])
|
"gnd"])
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
|
|
||||||
# bank select inverter
|
# bank select inverter
|
||||||
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
|
||||||
|
|
@ -195,7 +195,7 @@ class bank_select(design.design):
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
|
|
||||||
def route_modules(self):
|
def route_instances(self):
|
||||||
|
|
||||||
# bank_sel is vertical wire
|
# bank_sel is vertical wire
|
||||||
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class bitcell_array(design.design):
|
||||||
""" Create and connect the netlist """
|
""" Create and connect the netlist """
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ class bitcell_array(design.design):
|
||||||
self.cell = self.mod_bitcell()
|
self.cell = self.mod_bitcell()
|
||||||
self.add_mod(self.cell)
|
self.add_mod(self.cell)
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
""" Create the module instances used in this design """
|
""" Create the module instances used in this design """
|
||||||
self.cell_inst = {}
|
self.cell_inst = {}
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ class control_logic(design.design):
|
||||||
self.setup_signal_busses()
|
self.setup_signal_busses()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Create layout and route between modules """
|
""" Create layout and route between modules """
|
||||||
self.route_rails()
|
self.route_rails()
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route_all()
|
self.route_all()
|
||||||
|
|
||||||
#self.add_lvs_correspondence_points()
|
#self.add_lvs_correspondence_points()
|
||||||
|
|
@ -155,8 +155,8 @@ class control_logic(design.design):
|
||||||
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
|
self.rail_offsets = self.create_vertical_bus("metal2", self.m2_pitch, offset, self.internal_bus_list, height)
|
||||||
|
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
""" Create all the modules """
|
""" Create all the instances """
|
||||||
self.create_dffs()
|
self.create_dffs()
|
||||||
self.create_clk_row()
|
self.create_clk_row()
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
|
|
@ -167,8 +167,8 @@ class control_logic(design.design):
|
||||||
self.create_rbl()
|
self.create_rbl()
|
||||||
|
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
""" Place all the modules """
|
""" Place all the instances """
|
||||||
# Keep track of all right-most instances to determine row boundary
|
# Keep track of all right-most instances to determine row boundary
|
||||||
# and add the vdd/gnd pins
|
# and add the vdd/gnd pins
|
||||||
self.row_end_inst = []
|
self.row_end_inst = []
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,13 @@ class dff_buf(design.design):
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
self.width = self.dff.width + self.inv1.width + self.inv2.width
|
||||||
self.height = self.dff.height
|
self.height = self.dff.height
|
||||||
|
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route_wires()
|
self.route_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
@ -70,7 +70,7 @@ class dff_buf(design.design):
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
self.dff_inst=self.add_inst(name="dff_buf_dff",
|
self.dff_inst=self.add_inst(name="dff_buf_dff",
|
||||||
mod=self.dff)
|
mod=self.dff)
|
||||||
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
|
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
|
||||||
|
|
@ -83,7 +83,7 @@ class dff_buf(design.design):
|
||||||
mod=self.inv2)
|
mod=self.inv2)
|
||||||
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
# Add the DFF
|
# Add the DFF
|
||||||
self.dff_inst.place(vector(0,0))
|
self.dff_inst.place(vector(0,0))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ class hierarchical_predecode(design.design):
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def create_modules(self):
|
def add_modules(self):
|
||||||
""" Create the INV and NAND gate """
|
""" Add the INV and NAND gate modules """
|
||||||
|
|
||||||
self.inv = pinv()
|
self.inv = pinv()
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.add_modules()
|
||||||
self.create_input_inverters()
|
self.create_input_inverters()
|
||||||
self.create_output_inverters()
|
self.create_output_inverters()
|
||||||
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
|
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.add_modules()
|
||||||
self.create_input_inverters()
|
self.create_input_inverters()
|
||||||
self.create_output_inverters()
|
self.create_output_inverters()
|
||||||
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ class multibank(design.design):
|
||||||
|
|
||||||
self.compute_sizes()
|
self.compute_sizes()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
|
self.create_instances()
|
||||||
self.setup_layout_constraints()
|
self.setup_layout_constraints()
|
||||||
|
|
||||||
# FIXME: Move this to the add modules function
|
# FIXME: Move this to the add modules function
|
||||||
|
|
@ -111,7 +111,7 @@ class multibank(design.design):
|
||||||
|
|
||||||
self.route_supplies()
|
self.route_supplies()
|
||||||
|
|
||||||
def add_modules(self):
|
def create_instances(self):
|
||||||
""" Add modules. The order should not matter! """
|
""" Add modules. The order should not matter! """
|
||||||
|
|
||||||
# Above the bitcell array
|
# Above the bitcell array
|
||||||
|
|
@ -175,8 +175,8 @@ class multibank(design.design):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_modules(self):
|
def add_modules(self):
|
||||||
""" Create all the modules using the class loader """
|
""" Add all the modules using the class loader """
|
||||||
self.tri = self.mod_tri_gate()
|
self.tri = self.mod_tri_gate()
|
||||||
self.bitcell = self.mod_bitcell()
|
self.bitcell = self.mod_bitcell()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,11 @@ class replica_bitline(design.design):
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
self.create_modules()
|
self.create_instances()
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.calculate_module_offsets()
|
self.calculate_module_offsets()
|
||||||
self.place_modules()
|
self.place_instances()
|
||||||
self.route()
|
self.route()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ class replica_bitline(design.design):
|
||||||
self.access_tx = ptx(tx_type="pmos")
|
self.access_tx = ptx(tx_type="pmos")
|
||||||
self.add_mod(self.access_tx)
|
self.add_mod(self.access_tx)
|
||||||
|
|
||||||
def create_modules(self):
|
def create_instances(self):
|
||||||
""" Create all of the module instances in the logical netlist """
|
""" Create all of the module instances in the logical netlist """
|
||||||
|
|
||||||
# This is the threshold detect inverter on the output of the RBL
|
# This is the threshold detect inverter on the output of the RBL
|
||||||
|
|
@ -152,7 +152,7 @@ class replica_bitline(design.design):
|
||||||
self.wl_list = self.rbl.cell.list_all_wl_names()
|
self.wl_list = self.rbl.cell.list_all_wl_names()
|
||||||
self.bl_list = self.rbl.cell.list_all_bl_names()
|
self.bl_list = self.rbl.cell.list_all_bl_names()
|
||||||
|
|
||||||
def place_modules(self):
|
def place_instances(self):
|
||||||
""" Add all of the module instances in the logical netlist """
|
""" Add all of the module instances in the logical netlist """
|
||||||
|
|
||||||
# This is the threshold detect inverter on the output of the RBL
|
# This is the threshold detect inverter on the output of the RBL
|
||||||
|
|
|
||||||
|
|
@ -129,16 +129,15 @@ class wordline_driver(design.design):
|
||||||
nand2_xoffset = inv1_xoffset + self.inv.width
|
nand2_xoffset = inv1_xoffset + self.inv.width
|
||||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||||
|
|
||||||
self.width = inv2_xoffset + self.inv.height
|
self.width = inv2_xoffset + self.inv.width
|
||||||
driver_height = self.inv.height
|
|
||||||
self.height = self.inv.height * self.rows
|
self.height = self.inv.height * self.rows
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
if (row % 2):
|
if (row % 2):
|
||||||
y_offset = driver_height*(row + 1)
|
y_offset = self.inv.height*(row + 1)
|
||||||
inst_mirror = "MX"
|
inst_mirror = "MX"
|
||||||
else:
|
else:
|
||||||
y_offset = driver_height*row
|
y_offset = self.inv.height*row
|
||||||
inst_mirror = "R0"
|
inst_mirror = "R0"
|
||||||
|
|
||||||
inv1_offset = [inv1_xoffset, y_offset]
|
inv1_offset = [inv1_xoffset, y_offset]
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,10 @@ class options(optparse.Values):
|
||||||
debug_level = 0
|
debug_level = 0
|
||||||
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
# When enabled, layout is not generated (and no DRC or LVS are performed)
|
||||||
netlist_only = False
|
netlist_only = False
|
||||||
# This determines whether LVS and DRC is checked for each submodule.
|
# This determines whether LVS and DRC is checked at all.
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
|
# This determines whether LVS and DRC is checked for every submodule.
|
||||||
|
inline_lvsdrc = False
|
||||||
# Variable to select the variant of spice
|
# Variable to select the variant of spice
|
||||||
spice_name = ""
|
spice_name = ""
|
||||||
# The spice executable being used which is derived from the user PATH.
|
# The spice executable being used which is derived from the user PATH.
|
||||||
|
|
|
||||||
|
|
@ -653,63 +653,32 @@ class router(router_tech):
|
||||||
debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
|
debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
|
||||||
|
|
||||||
pin_set = self.pins[pin_name]
|
pin_set = self.pins[pin_name]
|
||||||
local_debug = False
|
|
||||||
|
|
||||||
# Put each pin in an equivalence class of it's own
|
# Put each pin in an equivalence class of it's own
|
||||||
equiv_classes = [set([x]) for x in pin_set]
|
equiv_classes = [set([x]) for x in pin_set]
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"INITIAL\n",equiv_classes)
|
|
||||||
|
|
||||||
def compare_classes(class1, class2):
|
|
||||||
"""
|
|
||||||
Determine if two classes should be combined and if so return
|
|
||||||
the combined set. Otherwise, return None.
|
|
||||||
"""
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"CLASS1:\n",class1)
|
|
||||||
debug.info(0,"CLASS2:\n",class2)
|
|
||||||
# Compare each pin in each class,
|
|
||||||
# and if any overlap, return the combined the class
|
|
||||||
for p1 in class1:
|
|
||||||
for p2 in class2:
|
|
||||||
if p1.overlaps(p2):
|
|
||||||
combined_class = class1 | class2
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"COMBINE:",pformat(combined_class))
|
|
||||||
return combined_class
|
|
||||||
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"NO COMBINE")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def combine_classes(equiv_classes):
|
def combine_classes(equiv_classes):
|
||||||
""" Recursive function to combine classes. """
|
|
||||||
local_debug = False
|
|
||||||
|
|
||||||
if local_debug:
|
|
||||||
debug.info(0,"\nRECURSE:\n",pformat(equiv_classes))
|
|
||||||
if len(equiv_classes)==1:
|
|
||||||
return(equiv_classes)
|
|
||||||
|
|
||||||
for class1 in equiv_classes:
|
for class1 in equiv_classes:
|
||||||
for class2 in equiv_classes:
|
for class2 in equiv_classes:
|
||||||
if class1 == class2:
|
if class1 == class2:
|
||||||
continue
|
continue
|
||||||
class3 = compare_classes(class1, class2)
|
# Compare each pin in each class,
|
||||||
if class3:
|
# and if any overlap, update equiv_classes to include the combined the class
|
||||||
new_classes = equiv_classes
|
for p1 in class1:
|
||||||
new_classes.remove(class1)
|
for p2 in class2:
|
||||||
new_classes.remove(class2)
|
if p1.overlaps(p2):
|
||||||
new_classes.append(class3)
|
combined_class = class1 | class2
|
||||||
return(combine_classes(new_classes))
|
equiv_classes.remove(class1)
|
||||||
else:
|
equiv_classes.remove(class2)
|
||||||
return(equiv_classes)
|
equiv_classes.append(combined_class)
|
||||||
|
return(equiv_classes)
|
||||||
|
return(equiv_classes)
|
||||||
|
|
||||||
reduced_classes = combine_classes(equiv_classes)
|
old_length = math.inf
|
||||||
if local_debug:
|
while (len(equiv_classes)<old_length):
|
||||||
debug.info(0,"FINAL ",reduced_classes)
|
old_length = len(equiv_classes)
|
||||||
self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in reduced_classes]
|
equiv_classes = combine_classes(equiv_classes)
|
||||||
|
|
||||||
|
self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in equiv_classes]
|
||||||
|
|
||||||
def convert_pins(self, pin_name):
|
def convert_pins(self, pin_name):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,14 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
self.bank_inst=self.create_bank(0)
|
self.bank_inst=self.create_bank(0)
|
||||||
|
|
||||||
self.control_logic_inst = self.create_control_logic()
|
self.control_logic_insts = self.create_control_logic()
|
||||||
|
|
||||||
self.row_addr_dff_inst = self.create_row_addr_dff()
|
self.row_addr_dff_insts = self.create_row_addr_dff()
|
||||||
|
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
self.col_addr_dff_inst = self.create_col_addr_dff()
|
self.col_addr_dff_insts = self.create_col_addr_dff()
|
||||||
|
|
||||||
self.data_dff_inst = self.create_data_dff()
|
self.data_dff_insts = self.create_data_dff()
|
||||||
|
|
||||||
def place_modules(self):
|
def place_modules(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,14 +58,14 @@ class sram_1bank(sram_base):
|
||||||
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
||||||
# up to the row address DFFs.
|
# up to the row address DFFs.
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
|
control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch,
|
||||||
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
|
self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y)
|
||||||
self.control_logic_inst[port].place(control_pos)
|
self.control_logic_insts[port].place(control_pos)
|
||||||
|
|
||||||
# The row address bits are placed above the control logic aligned on the right.
|
# The row address bits are placed above the control logic aligned on the right.
|
||||||
row_addr_pos = vector(self.control_logic_inst[0].rx() - self.row_addr_dff.width,
|
row_addr_pos = vector(self.control_logic_insts[0].rx() - self.row_addr_dff.width,
|
||||||
self.control_logic_inst[0].uy())
|
self.control_logic_insts[0].uy())
|
||||||
self.row_addr_dff_inst[port].place(row_addr_pos)
|
self.row_addr_dff_insts[port].place(row_addr_pos)
|
||||||
|
|
||||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||||
data_gap = -self.m2_pitch*(self.word_size+1)
|
data_gap = -self.m2_pitch*(self.word_size+1)
|
||||||
|
|
@ -75,7 +75,7 @@ class sram_1bank(sram_base):
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
col_addr_pos = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
|
||||||
data_gap - self.col_addr_dff.height)
|
data_gap - self.col_addr_dff.height)
|
||||||
self.col_addr_dff_inst[port].place(col_addr_pos)
|
self.col_addr_dff_insts[port].place(col_addr_pos)
|
||||||
|
|
||||||
# Add the data flops below the bank to the right of the center of bank:
|
# Add the data flops below the bank to the right of the center of bank:
|
||||||
# This relies on the center point of the bank:
|
# This relies on the center point of the bank:
|
||||||
|
|
@ -84,7 +84,7 @@ class sram_1bank(sram_base):
|
||||||
# sense amps.
|
# sense amps.
|
||||||
data_pos = vector(self.bank.bank_center.x,
|
data_pos = vector(self.bank.bank_center.x,
|
||||||
data_gap - self.data_dff.height)
|
data_gap - self.data_dff.height)
|
||||||
self.data_dff_inst[port].place(data_pos)
|
self.data_dff_insts[port].place(data_pos)
|
||||||
|
|
||||||
# two supply rails are already included in the bank, so just 2 here.
|
# two supply rails are already included in the bank, so just 2 here.
|
||||||
# self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
# self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
|
||||||
|
|
@ -97,7 +97,7 @@ class sram_1bank(sram_base):
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
# Connect the control pins as inputs
|
# Connect the control pins as inputs
|
||||||
for signal in self.control_logic_inputs[port] + ["clk"]:
|
for signal in self.control_logic_inputs[port] + ["clk"]:
|
||||||
self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port))
|
self.copy_layout_pin(self.control_logic_insts[port], signal, signal+"{}".format(port))
|
||||||
|
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
|
|
@ -105,14 +105,14 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Lower address bits
|
# Lower address bits
|
||||||
for bit in range(self.col_addr_size):
|
for bit in range(self.col_addr_size):
|
||||||
self.copy_layout_pin(self.col_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit))
|
self.copy_layout_pin(self.col_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit))
|
||||||
# Upper address bits
|
# Upper address bits
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
self.copy_layout_pin(self.row_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size))
|
self.copy_layout_pin(self.row_addr_dff_insts[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size))
|
||||||
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit))
|
self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit))
|
||||||
|
|
||||||
def route(self):
|
def route(self):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
@ -135,7 +135,7 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# This is the actual input to the SRAM
|
# This is the actual input to the SRAM
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port))
|
self.copy_layout_pin(self.control_logic_insts[port], "clk", "clk{}".format(port))
|
||||||
|
|
||||||
# Connect all of these clock pins to the clock in the central bus
|
# Connect all of these clock pins to the clock in the central bus
|
||||||
# This is something like a "spine" clock distribution. The two spines
|
# This is something like a "spine" clock distribution. The two spines
|
||||||
|
|
@ -147,23 +147,23 @@ class sram_1bank(sram_base):
|
||||||
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
|
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
|
||||||
|
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
dff_clk_pin = self.col_addr_dff_inst[port].get_pin("clk")
|
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
||||||
dff_clk_pos = dff_clk_pin.center()
|
dff_clk_pos = dff_clk_pin.center()
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
|
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
|
||||||
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
||||||
|
|
||||||
data_dff_clk_pin = self.data_dff_inst[port].get_pin("clk")
|
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
||||||
data_dff_clk_pos = data_dff_clk_pin.center()
|
data_dff_clk_pos = data_dff_clk_pin.center()
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
|
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
|
||||||
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
||||||
|
|
||||||
# This uses a metal2 track to the right of the control/row addr DFF
|
# This uses a metal2 track to the right of the control/row addr DFF
|
||||||
# to route vertically.
|
# to route vertically.
|
||||||
control_clk_buf_pin = self.control_logic_inst[port].get_pin("clk_buf")
|
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
|
||||||
control_clk_buf_pos = control_clk_buf_pin.rc()
|
control_clk_buf_pos = control_clk_buf_pin.rc()
|
||||||
row_addr_clk_pin = self.row_addr_dff_inst[port].get_pin("clk")
|
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
|
||||||
row_addr_clk_pos = row_addr_clk_pin.rc()
|
row_addr_clk_pos = row_addr_clk_pin.rc()
|
||||||
mid1_pos = vector(self.row_addr_dff_inst[port].rx() + self.m2_pitch,
|
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
|
||||||
row_addr_clk_pos.y)
|
row_addr_clk_pos.y)
|
||||||
mid2_pos = vector(mid1_pos.x,
|
mid2_pos = vector(mid1_pos.x,
|
||||||
control_clk_buf_pos.y)
|
control_clk_buf_pos.y)
|
||||||
|
|
@ -176,7 +176,7 @@ class sram_1bank(sram_base):
|
||||||
""" Route the outputs from the control logic module """
|
""" Route the outputs from the control logic module """
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for signal in self.control_logic_outputs[port]:
|
for signal in self.control_logic_outputs[port]:
|
||||||
src_pin = self.control_logic_inst[port].get_pin(signal)
|
src_pin = self.control_logic_insts[port].get_pin(signal)
|
||||||
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
||||||
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
|
@ -190,7 +190,7 @@ class sram_1bank(sram_base):
|
||||||
for bit in range(self.row_addr_size):
|
for bit in range(self.row_addr_size):
|
||||||
flop_name = "dout_{}".format(bit)
|
flop_name = "dout_{}".format(bit)
|
||||||
bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size)
|
bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size)
|
||||||
flop_pin = self.row_addr_dff_inst[port].get_pin(flop_name)
|
flop_pin = self.row_addr_dff_insts[port].get_pin(flop_name)
|
||||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||||
flop_pos = flop_pin.center()
|
flop_pos = flop_pin.center()
|
||||||
bank_pos = bank_pin.center()
|
bank_pos = bank_pin.center()
|
||||||
|
|
@ -206,13 +206,13 @@ class sram_1bank(sram_base):
|
||||||
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
|
||||||
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
|
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
|
||||||
pitch=self.m1_pitch,
|
pitch=self.m1_pitch,
|
||||||
offset=self.col_addr_dff_inst[port].ul() + vector(0, self.m1_pitch),
|
offset=self.col_addr_dff_insts[port].ul() + vector(0, self.m1_pitch),
|
||||||
names=bus_names,
|
names=bus_names,
|
||||||
length=self.col_addr_dff_inst[port].width)
|
length=self.col_addr_dff_insts[port].width)
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
|
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
|
||||||
data_dff_map = zip(dff_names, bus_names)
|
data_dff_map = zip(dff_names, bus_names)
|
||||||
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_inst[port], col_addr_bus_offsets)
|
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets)
|
||||||
|
|
||||||
bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)]
|
bank_names = ["addr{0}_{1}".format(port,x) for x in range(self.col_addr_size)]
|
||||||
data_bank_map = zip(bank_names, bus_names)
|
data_bank_map = zip(bank_names, bus_names)
|
||||||
|
|
@ -223,13 +223,13 @@ class sram_1bank(sram_base):
|
||||||
""" Connect the output of the data flops to the write driver """
|
""" Connect the output of the data flops to the write driver """
|
||||||
# This is where the channel will start (y-dimension at least)
|
# This is where the channel will start (y-dimension at least)
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
offset = self.data_dff_inst[port].ul() + vector(0, 2*self.m1_pitch)
|
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||||
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
||||||
|
|
||||||
route_map = list(zip(bank_names, dff_names))
|
route_map = list(zip(bank_names, dff_names))
|
||||||
dff_pins = {key: self.data_dff_inst[port].get_pin(key) for key in dff_names }
|
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names }
|
||||||
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
|
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
|
||||||
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
||||||
all_pins = {**dff_pins, **bank_pins}
|
all_pins = {**dff_pins, **bank_pins}
|
||||||
|
|
@ -245,7 +245,7 @@ class sram_1bank(sram_base):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for n in self.control_logic_outputs[0]:
|
for n in self.control_logic_outputs[0]:
|
||||||
pin = self.control_logic_inst[0].get_pin(n)
|
pin = self.control_logic_insts[0].get_pin(n)
|
||||||
self.add_label(text=n,
|
self.add_label(text=n,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
offset=pin.center())
|
offset=pin.center())
|
||||||
|
|
|
||||||
|
|
@ -216,20 +216,24 @@ class sram_base(design):
|
||||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||||
self.bitcell = self.mod_bitcell()
|
self.bitcell = self.mod_bitcell()
|
||||||
|
|
||||||
#c = reload(__import__(OPTS.control_logic))
|
c = reload(__import__(OPTS.control_logic))
|
||||||
#self.mod_control_logic = getattr(c, OPTS.control_logic)
|
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||||
|
|
||||||
|
|
||||||
from control_logic import control_logic
|
|
||||||
# Create the control logic module for each port type
|
# Create the control logic module for each port type
|
||||||
if OPTS.num_rw_ports>0:
|
if len(self.readwrite_ports)>0:
|
||||||
self.control_logic = self.control_logic_rw = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="rw")
|
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
words_per_row=self.words_per_row,
|
||||||
|
port_type="rw")
|
||||||
self.add_mod(self.control_logic_rw)
|
self.add_mod(self.control_logic_rw)
|
||||||
if OPTS.num_w_ports>0:
|
if len(self.write_ports)>0:
|
||||||
self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w")
|
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
words_per_row=self.words_per_row,
|
||||||
|
port_type="w")
|
||||||
self.add_mod(self.control_logic_w)
|
self.add_mod(self.control_logic_w)
|
||||||
if OPTS.num_r_ports>0:
|
if len(self.read_ports)>0:
|
||||||
self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r")
|
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
|
||||||
|
words_per_row=self.words_per_row,
|
||||||
|
port_type="r")
|
||||||
self.add_mod(self.control_logic_r)
|
self.add_mod(self.control_logic_r)
|
||||||
|
|
||||||
# Create the address and control flops (but not the clk)
|
# Create the address and control flops (but not the clk)
|
||||||
|
|
@ -382,7 +386,8 @@ class sram_base(design):
|
||||||
|
|
||||||
|
|
||||||
def create_control_logic(self):
|
def create_control_logic(self):
|
||||||
""" Add and place control logic """
|
""" Add control logic instances """
|
||||||
|
|
||||||
insts = []
|
insts = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
|
|
@ -392,8 +397,7 @@ class sram_base(design):
|
||||||
else:
|
else:
|
||||||
mod = self.control_logic_r
|
mod = self.control_logic_r
|
||||||
|
|
||||||
insts.append(self.add_inst(name="control{}".format(port),
|
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
|
||||||
mod=mod))
|
|
||||||
|
|
||||||
temp = ["csb{}".format(port)]
|
temp = ["csb{}".format(port)]
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run a regression test on 1rw 1r sram bank
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header,openram_test
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class single_bank_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
from bank import bank
|
||||||
|
|
||||||
|
OPTS.bitcell = "bitcell_1rw_1r"
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
|
||||||
|
from sram_config import sram_config
|
||||||
|
c = sram_config(word_size=4,
|
||||||
|
num_words=16)
|
||||||
|
|
||||||
|
c.words_per_row=1
|
||||||
|
debug.info(1, "No column mux")
|
||||||
|
a = bank(c, name="bank1_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
c.num_words=32
|
||||||
|
c.words_per_row=2
|
||||||
|
debug.info(1, "Two way column mux")
|
||||||
|
a = bank(c, name="bank2_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
c.num_words=64
|
||||||
|
c.words_per_row=4
|
||||||
|
debug.info(1, "Four way column mux")
|
||||||
|
a = bank(c, name="bank3_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
c.word_size=2
|
||||||
|
c.num_words=128
|
||||||
|
c.words_per_row=8
|
||||||
|
debug.info(1, "Eight way column mux")
|
||||||
|
a = bank(c, name="bank4_single")
|
||||||
|
self.local_check(a)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 414 KiB |
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="112" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="112" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h71v20H0z"/><path fill="#007ec6" d="M71 0h41v20H71z"/><path fill="url(#b)" d="M0 0h112v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"><text x="365" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="610">download </text><text x="365" y="140" transform="scale(.1)" textLength="610">download </text><text x="905" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="310">latest</text><text x="905" y="140" transform="scale(.1)" textLength="310">latest</text></g> </svg>
|
||||||
|
After Width: | Height: | Size: 1004 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="136" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="a"><rect width="136" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h51v20H0z"/><path fill="#007ec6" d="M51 0h85v20H51z"/><path fill="url(#b)" d="M0 0h136v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">License</text><text x="265" y="140" transform="scale(.1)" textLength="410">License</text><text x="925" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="750">BSD 3-Clause</text><text x="925" y="140" transform="scale(.1)" textLength="750">BSD 3-Clause</text></g> </svg>
|
||||||
|
After Width: | Height: | Size: 971 B |
Loading…
Reference in New Issue