merge dev into datasheet_gen; fixed merge conflict in hierarchy_design.py

This commit is contained in:
Jesse Cirimelli-Low 2018-11-15 10:45:33 -08:00
commit 59c0421804
24 changed files with 1014 additions and 634 deletions

View File

@ -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
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
Our code may not be the best and we acknowledge that. We welcome

110
HINTS.md Normal file
View File

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

@ -1,238 +1,214 @@
# BASIC SETUP
# OpenRAM
[![pipeline status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/dev/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits)
[![Download](images/download.svg)](https://github.com/VLSIDA/PrivateRAM/archive/dev.zip)
[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE)
Please look at the OpenRAM ICCAD paper and presentation in the repository:
https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_paper.pdf
https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_presentation.pdf
An open-source static random access memory (SRAM) compiler.
# 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:
* ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
* Python 3.5 and higher
* Python numpy (pip3 install numpy 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
+ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
+ Python 3.5 or higher
+ Python numpy (pip3 install numpy to install)
+ flask_table (pip3 install flask to install)
If you want to perform DRC and LVS, you will need either:
* Calibre (for FreePDK45 or SCMOS)
* Magic + Netgen (for SCMOS only)
+ Calibre (for [FreePDK45])
+ [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_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
spice models, however, are generic and should be replaced with foundry
models.
If you are using FreePDK, you should also have that set up and have the
environment variable point to the PDK.
For example, in bash, add to your .bashrc:
We include the tech files necessary for [FreePDK45] and [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:
```
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:
http://opencircuitdesign.com/magic/
http://opencircuitdesign.com/netgen/
We have included the SCN4M design rules from QFlow:
http://opencircuitdesign.com/qflow/
# 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
You can then run OpenRAM by executing:
```
python3 $OPENRAM\_HOME/openram.py myconfig
```
You can see all of the options for the configuration file in
$OPENRAM\_HOME/options.py
# UNIT TESTS
# Unit Tests
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:
```
python {unit test}.py
python3 {unit test}.py
```
The unit tests take the same arguments as openram.py itself.
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
"-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.
# CREATING CUSTOM TECHNOLOGIES
# Porting to a New Technology
All setup scripts should be in the setup_scripts directory under the
$OPENRAM_TECH directory. Please look at the following file for an
If you want to support a enw 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
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:
```
$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:
1. gds_lib folder with all the .gds (premade) library cells. At a
minimum this includes:
* ms_flop.gds
* sense_amp.gds
* write_driver.gds
* cell_6t.gds
* replica_cell_6t.gds
* tri_gate.gds
2. sp_lib folder with all the .sp (premade) library netlists for the above cells.
3. layers.map
4. 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
* etc.
* gds_lib folder with all the .gds (premade) library cells:
* dff.gds
* sense_amp.gds
* write_driver.gds
* cell_6t.gds
* replica\_cell\_6t.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.
# DEBUGGING
# Get Involved
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 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
+ Report bugs by submitting [Github issues].
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
+ Submit code/fixes using a [Github pull request]
+ Follow our [project][Github projects].
+ Read and cite our [ICCAD paper][OpenRAMpaper]
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)
# Further Help
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.
+ [Additional hints](./HINTS.md)
+ [OpenRAM Slack Workspace][Slack]
+ [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
file. If the DRC fails, it will typically show you the command that was used
to run Calibre or Magic+Netgen.
# License
To debug, you will need a layout viewer. I prefer to use Glade
on my Mac, but you can also use Calibre, Magic, etc.
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
1. Calibre
# Contributors & Acknowledgment
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.
- [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
- [James Stine] from [VLSIARCH] co-founded the project.
- Hunter Nichols maintains and updates the timing characterization.
- Michael Grims created and maintains the multiport netlist code.
- Jennifer Sowash is creating the OpenRAM IP library.
- Jesse Cirimelli-Low created the datasheet generation.
- Samira Ataei created early multi-bank layouts and control logic.
- Bin Wu created early parameterized cells.
- Yusu Wang is porting parameterized cells to new technologies.
- 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.
You can get it from: http://www.peardrop.co.uk/glade/
[Github issues]: https://github.com/PrivateRAM/PrivateRAM/issues
[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
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).
[email me]: mailto:mrg+openram@ucsc.edu
[dev-group]: mailto:openram-dev-group@ucsc.edu
[user-group]: mailto:openram-user-group@ucsc.edu
[dev-group-subscribe]: mailto:openram-dev-group+subscribe@ucsc.edu
[user-group-subscribe]: mailto:openram-user-group+subscribe@ucsc.edu
To load the errors, you simply do Verify->Import Calibre Errors select
the .results file from Calibre.
[Magic]: http://opencircuitdesign.com/magic/
[Netgen]: http://opencircuitdesign.com/netgen/
[Qflow]: http://opencircuitdesign.com/qflow/history.html
[Ngspice]: http://ngspice.sourceforge.net/
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.
[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
[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LTE4ODMyM2I0Mzk2ZmFiMjgwYTYyMTQ4NTgwMmUwMDhiM2E1MDViNDRjYzU1NjJhZTQxNWZjMzE3M2FlODBmZjA

View File

@ -70,40 +70,47 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""Checks both DRC and LVS for a module"""
# Unit tests will check themselves.
# 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_lvs_errors
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
self.sp_write(tempspice)
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_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors))
total_drc_errors += num_drc_errors
total_lvs_errors += num_lvs_errors
os.remove(tempspice)
os.remove(tempgds)
def DRC(self):
def DRC(self, final_verification=False):
"""Checks DRC for a module"""
# Unit tests will check themselves.
# 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
tempgds = OPTS.openram_temp + "/temp.gds"
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
debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error))
os.remove(tempgds)
def LVS(self, final_verification=False):
"""Checks LVS for a module"""
# Unit tests will check themselves.
# 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
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"

View File

@ -30,8 +30,10 @@ def parse_args():
help="Base output file name(s) prefix", metavar="FILE"),
optparse.make_option("-p", "--outpath", dest="output_path",
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",
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",
help="Increase the verbosity level"),
optparse.make_option("-t", "--tech", dest="tech_name",
@ -388,10 +390,10 @@ def import_tech():
def print_time(name, now_time, last_time=None):
""" Print a statement about the time delta. """
if last_time:
time = round((now_time-last_time).total_seconds(),1)
time = str(round((now_time-last_time).total_seconds(),1)) + " seconds"
else:
time = now_time
print("** {0}: {1} seconds".format(name,time))
time = now_time.strftime('%m/%d/%Y %H:%M:%S')
print("** {0}: {1}".format(name,time))
def report_status():
@ -414,6 +416,9 @@ def report_status():
if OPTS.netlist_only:
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:
print("DRC/LVS/PEX checking is disabled.")
print("DRC/LVS/PEX is completely disabled.")

File diff suppressed because it is too large Load Diff

View File

@ -27,12 +27,12 @@ class bank_select(design.design):
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_modules()
self.create_instances()
def create_layout(self):
self.calculate_module_offsets()
self.place_modules()
self.route_modules()
self.place_instances()
self.route_instances()
self.DRC_LVS()
@ -99,7 +99,7 @@ class bank_select(design.design):
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
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",
mod=self.inv_sel)
@ -152,7 +152,7 @@ class bank_select(design.design):
"vdd",
"gnd"])
def place_modules(self):
def place_instances(self):
# bank select inverter
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
@ -195,7 +195,7 @@ class bank_select(design.design):
mirror=mirror)
def route_modules(self):
def route_instances(self):
# bank_sel is vertical wire
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")

View File

@ -34,7 +34,7 @@ class bitcell_array(design.design):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_modules()
self.create_instances()
def create_layout(self):
@ -85,7 +85,7 @@ class bitcell_array(design.design):
self.cell = self.mod_bitcell()
self.add_mod(self.cell)
def create_modules(self):
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
for col in range(self.column_size):

View File

@ -41,12 +41,12 @@ class control_logic(design.design):
self.setup_signal_busses()
self.add_pins()
self.add_modules()
self.create_modules()
self.create_instances()
def create_layout(self):
""" Create layout and route between modules """
self.route_rails()
self.place_modules()
self.place_instances()
self.route_all()
#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)
def create_modules(self):
""" Create all the modules """
def create_instances(self):
""" Create all the instances """
self.create_dffs()
self.create_clk_row()
if (self.port_type == "rw") or (self.port_type == "w"):
@ -167,8 +167,8 @@ class control_logic(design.design):
self.create_rbl()
def place_modules(self):
""" Place all the modules """
def place_instances(self):
""" Place all the instances """
# Keep track of all right-most instances to determine row boundary
# and add the vdd/gnd pins
self.row_end_inst = []

View File

@ -36,13 +36,13 @@ class dff_buf(design.design):
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_modules()
self.create_instances()
def create_layout(self):
self.width = self.dff.width + self.inv1.width + self.inv2.width
self.height = self.dff.height
self.place_modules()
self.place_instances()
self.route_wires()
self.add_layout_pins()
self.DRC_LVS()
@ -70,7 +70,7 @@ class dff_buf(design.design):
self.add_pin("vdd")
self.add_pin("gnd")
def create_modules(self):
def create_instances(self):
self.dff_inst=self.add_inst(name="dff_buf_dff",
mod=self.dff)
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
@ -83,7 +83,7 @@ class dff_buf(design.design):
mod=self.inv2)
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
def place_modules(self):
def place_instances(self):
# Add the DFF
self.dff_inst.place(vector(0,0))

View File

@ -31,8 +31,8 @@ class hierarchical_predecode(design.design):
self.add_pin("vdd")
self.add_pin("gnd")
def create_modules(self):
""" Create the INV and NAND gate """
def add_modules(self):
""" Add the INV and NAND gate modules """
self.inv = pinv()
self.add_mod(self.inv)

View File

@ -18,7 +18,7 @@ class hierarchical_predecode2x4(hierarchical_predecode):
def create_netlist(self):
self.add_pins()
self.create_modules()
self.add_modules()
self.create_input_inverters()
self.create_output_inverters()
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"],

View File

@ -18,7 +18,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
def create_netlist(self):
self.add_pins()
self.create_modules()
self.add_modules()
self.create_input_inverters()
self.create_output_inverters()
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],

View File

@ -54,8 +54,8 @@ class multibank(design.design):
self.compute_sizes()
self.add_pins()
self.create_modules()
self.add_modules()
self.create_instances()
self.setup_layout_constraints()
# FIXME: Move this to the add modules function
@ -111,7 +111,7 @@ class multibank(design.design):
self.route_supplies()
def add_modules(self):
def create_instances(self):
""" Add modules. The order should not matter! """
# Above the bitcell array
@ -175,8 +175,8 @@ class multibank(design.design):
def create_modules(self):
""" Create all the modules using the class loader """
def add_modules(self):
""" Add all the modules using the class loader """
self.tri = self.mod_tri_gate()
self.bitcell = self.mod_bitcell()

View File

@ -29,11 +29,11 @@ class replica_bitline(design.design):
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_modules()
self.create_instances()
def create_layout(self):
self.calculate_module_offsets()
self.place_modules()
self.place_instances()
self.route()
self.add_layout_pins()
@ -104,7 +104,7 @@ class replica_bitline(design.design):
self.access_tx = ptx(tx_type="pmos")
self.add_mod(self.access_tx)
def create_modules(self):
def create_instances(self):
""" Create all of the module instances in the logical netlist """
# 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.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 """
# This is the threshold detect inverter on the output of the RBL

View File

@ -129,16 +129,15 @@ class wordline_driver(design.design):
nand2_xoffset = inv1_xoffset + self.inv.width
inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.height
driver_height = self.inv.height
self.width = inv2_xoffset + self.inv.width
self.height = self.inv.height * self.rows
for row in range(self.rows):
if (row % 2):
y_offset = driver_height*(row + 1)
y_offset = self.inv.height*(row + 1)
inst_mirror = "MX"
else:
y_offset = driver_height*row
y_offset = self.inv.height*row
inst_mirror = "R0"
inv1_offset = [inv1_xoffset, y_offset]

View File

@ -20,8 +20,10 @@ class options(optparse.Values):
debug_level = 0
# When enabled, layout is not generated (and no DRC or LVS are performed)
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
# This determines whether LVS and DRC is checked for every submodule.
inline_lvsdrc = False
# Variable to select the variant of spice
spice_name = ""
# The spice executable being used which is derived from the user PATH.

View File

@ -653,63 +653,32 @@ class router(router_tech):
debug.info(2,"Analyzing pin groups for {}.".format(pin_name))
pin_set = self.pins[pin_name]
local_debug = False
# Put each pin in an equivalence class of it's own
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):
""" 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 class2 in equiv_classes:
if class1 == class2:
continue
class3 = compare_classes(class1, class2)
if class3:
new_classes = equiv_classes
new_classes.remove(class1)
new_classes.remove(class2)
new_classes.append(class3)
return(combine_classes(new_classes))
else:
return(equiv_classes)
# Compare each pin in each class,
# and if any overlap, update equiv_classes to include the combined the class
for p1 in class1:
for p2 in class2:
if p1.overlaps(p2):
combined_class = class1 | class2
equiv_classes.remove(class1)
equiv_classes.remove(class2)
equiv_classes.append(combined_class)
return(equiv_classes)
return(equiv_classes)
reduced_classes = combine_classes(equiv_classes)
if local_debug:
debug.info(0,"FINAL ",reduced_classes)
self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_set=x, router=self) for x in reduced_classes]
old_length = math.inf
while (len(equiv_classes)<old_length):
old_length = len(equiv_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):
"""

View File

@ -34,14 +34,14 @@ class sram_1bank(sram_base):
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:
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):
"""
@ -58,14 +58,14 @@ class sram_1bank(sram_base):
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs.
for port in self.all_ports:
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
self.control_logic_inst[port].place(control_pos)
control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch,
self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y)
self.control_logic_insts[port].place(control_pos)
# 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,
self.control_logic_inst[0].uy())
self.row_addr_dff_inst[port].place(row_addr_pos)
row_addr_pos = vector(self.control_logic_insts[0].rx() - self.row_addr_dff.width,
self.control_logic_insts[0].uy())
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
data_gap = -self.m2_pitch*(self.word_size+1)
@ -75,7 +75,7 @@ class sram_1bank(sram_base):
if self.col_addr_dff:
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)
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:
# This relies on the center point of the bank:
@ -84,7 +84,7 @@ class sram_1bank(sram_base):
# sense amps.
data_pos = vector(self.bank.bank_center.x,
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.
# 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:
# Connect the control pins as inputs
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:
for bit in range(self.word_size):
@ -105,14 +105,14 @@ class sram_1bank(sram_base):
# Lower address bits
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
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:
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):
""" Route a single bank SRAM """
@ -135,7 +135,7 @@ class sram_1bank(sram_base):
# This is the actual input to the SRAM
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
# 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()
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()
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])
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()
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])
# This uses a metal2 track to the right of the control/row addr DFF
# 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()
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()
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)
mid2_pos = vector(mid1_pos.x,
control_clk_buf_pos.y)
@ -176,7 +176,7 @@ class sram_1bank(sram_base):
""" Route the outputs from the control logic module """
for port in self.all_ports:
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))
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
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):
flop_name = "dout_{}".format(bit)
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)
flop_pos = flop_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)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
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,
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)]
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)]
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 """
# This is where the channel will start (y-dimension at least)
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)]
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
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 }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**dff_pins, **bank_pins}
@ -245,7 +245,7 @@ class sram_1bank(sram_base):
"""
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,
layer=pin.layer,
offset=pin.center())

View File

@ -216,20 +216,24 @@ class sram_base(design):
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
#c = reload(__import__(OPTS.control_logic))
#self.mod_control_logic = getattr(c, OPTS.control_logic)
c = reload(__import__(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
if OPTS.num_rw_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")
if len(self.readwrite_ports)>0:
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
port_type="rw")
self.add_mod(self.control_logic_rw)
if OPTS.num_w_ports>0:
self.control_logic_w = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="w")
if len(self.write_ports)>0:
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)
if OPTS.num_r_ports>0:
self.control_logic_r = control_logic(num_rows=self.num_rows, words_per_row=self.words_per_row, port_type="r")
if len(self.read_ports)>0:
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)
# Create the address and control flops (but not the clk)
@ -382,7 +386,8 @@ class sram_base(design):
def create_control_logic(self):
""" Add and place control logic """
""" Add control logic instances """
insts = []
for port in self.all_ports:
if port in self.readwrite_ports:
@ -392,8 +397,7 @@ class sram_base(design):
else:
mod = self.control_logic_r
insts.append(self.add_inst(name="control{}".format(port),
mod=mod))
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
temp = ["csb{}".format(port)]
if port in self.readwrite_ports:

View File

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

BIN
images/SCMOS_16kb_sram.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

2
images/download.svg Normal file
View File

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

1
images/license_badge.svg Normal file
View File

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