Merge branch 'dev' into pdriver

This commit is contained in:
Jennifer Eve Sowash 2018-11-26 13:15:47 -08:00
commit 524334d24d
39 changed files with 929 additions and 893 deletions

14
.coveragerc Normal file
View File

@ -0,0 +1,14 @@
[run]
omit =
# omit anything in a .local directory anywhere
*/.local/*
# omit everything in /usr
/usr/*
[paths]
source =
/home/gitlab-runner/builds/2fd64746/0
/home/gitlab-runner/builds/2fd64746/1
/home/gitlab-runner/builds/2fd64746/2
/home/gitlab-runner/builds/2fd64746/3
/home/gitlab-runner/builds/2fd64746/4
/home/gitlab-runner/builds/2fd64746/5

View File

@ -1,6 +1,39 @@
before_script:
- . /home/gitlab-runner/setup-paths.sh
- export OPENRAM_HOME="`pwd`/compiler"
- export OPENRAM_TECH="`pwd`/technology"
stages:
- test
- coverage
freepdk45: freepdk45:
script: "/home/gitlab-runner/regress_freepdk45.sh" stage: test
script:
- coverage run -p $OPENRAM_HOME/tests/regress.py -t freepdk45
artifacts:
paths:
- .coverage.*
expire_in: 1 week
scn4m_subm: scn4m_subm:
script: "/home/gitlab-runner/regress_scn4m_subm.sh" stage: test
script:
- coverage run -p $OPENRAM_HOME/tests/regress.py -t scn4m_subm
artifacts:
paths:
- .coverage.*
expire_in: 1 week
coverage:
stage: coverage
script:
- coverage combine
- coverage report
- coverage html -d coverage_html
artifacts:
paths:
- coverage_html
expire_in: 1 week
coverage: '/TOTAL.+ ([0-9]{1,3}%)/'

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

196
README.md
View File

@ -1,83 +1,119 @@
# OpenRAM # 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) [![Python 3.5](https://img.shields.io/badge/Python-3.5-green.svg)](https://www.python.org/)
[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE) [![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE)
Master:
[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/master/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits/master)
![Coverage](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/master/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV)
[![Download](./images/download-stable-blue.svg)](https://github.com/VLSIDA/PrivateRAM/archive/master.zip)
Dev:
[![Pipeline Status](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/dev/pipeline.svg?private_token=ynB6rSFLzvKUseoBPcwV)](https://github.com/VLSIDA/PrivateRAM/commits/dev)
![Coverage](https://scone.soe.ucsc.edu:8888/mrg/PrivateRAM/badges/dev/coverage.svg?private_token=ynB6rSFLzvKUseoBPcwV)
[![Download](./images/download-unstable-blue.svg)](https://github.com/VLSIDA/PrivateRAM/archive/dev.zip)
An open-source static random access memory (SRAM) compiler. An open-source static random access memory (SRAM) compiler.
# Why OpenRAM? # 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 # 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)
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:
You may also wish to add OPENRAM\_HOME to your PYTHONPATH:
``` ```
setenv OPENRAM_HOME "$HOME/openram/compiler" export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME"
setenv OPENRAM_TECH "$HOME/openram/technology"
``` ```
We include the tech files necessary for [FreePDK45] 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 [FreePDK45], 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:
```
setenv FREEPDK45 "/bsoe/software/design-kits/FreePDK45"
```
We do not distribute the PDK, but you may download [FreePDK45]
You may get the entire [FreePDK45 PDK here][FreePDK45].
If you are using [SCMOS], you should install [Magic] and [Netgen]. If you are using [SCMOS], you should install [Magic] and [Netgen].
We have included the SCN4M design rules from [Qflow]. We have included the most recent SCN4M_SUBM design rules from [Qflow].
# Directory Structure # Basic Usage
* compiler - openram compiler itself (pointed to by OPENRAM_HOME) Once you have defined the environment, you can run OpenRAM from the command line
* compiler/base - base data structure modules using a single configuration file written in Python.
* compiler/pgates - parameterized cells (e.g. logic gates)
* compiler/bitcells - various bitcell styles For example, create a file called *myconfig.py* specifying the following
* compiler/modules - high-level modules (e.g. decoders, etc.) parameters for your memory:
* compiler/verify - DRC and LVS verification wrappers
* compiler/characterizer - timing characterization code ```
* compiler/gdsMill - GDSII reader/writer # Data word size
* compiler/router - router for signals and power supplies word_size = 2
* compiler/tests - unit tests # Number of words in the memory
* technology - openram technology directory (pointed to by OPENRAM_TECH) num_words = 16
* technology/freepdk45 - example configuration library for [FreePDK45 technology node
* technology/scn4m_subm - example configuration library [SCMOS] technology node # Technology to use in $OPENRAM_TECH
* technology/scn3me_subm - unsupported configuration (not enough metal layers) tech_name = "scn4m_subm"
* technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies # Process corners to characterize
* docs - LaTeX manual (outdated) process_corners = ["TT"]
* lib - IP library of pregenerated memories # 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
```
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. 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:
``` ```
python3 regress.py python3 regress.py
``` ```
@ -96,68 +132,100 @@ To specify a particular technology use "-t <techname>" such as
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
If you want to support a enw technology, you will need to create: If you want to support a enw technology, you will need to create:
+ a setup script for each technology you want to use + a setup script for each technology you want to use
+ a technology directory for each technology with the base cells + a technology directory for each technology with the base cells
All setup scripts should be in the setup\_scripts directory under the All setup scripts should be in the setup\_scripts directory under the
$OPENRAM\_TECH directory. We provide two technology examples for [SCMOS] and [FreePDK45]. $OPENRAM\_TECH directory. We provide two technology examples for
Please look at the following file for an example of what is needed for OpenRAM: [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 $OPENRAM_TECH/setup_scripts/setup_openram_freepdk45.py
``` ```
Each setup script should be named as: setup\_openram\_{tech name}.py. Each setup script should be named as: setup\_openram\_{tech name}.py.
Each specific technology (e.g., [FreePDK45]) should be a subdirectory 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
* tri_gate.gds * sp_lib folder with all the .sp (premade) library netlists for the above cells.
2. sp_lib folder with all the .sp (premade) library netlists for the above cells. * layers.map
3. layers.map * A valid tech Python module (tech directory with __init__.py and tech.py) with:
4. A valid tech Python module (tech directory with __init__.py and tech.py) with:
* References in tech.py to spice models * References in tech.py to spice models
* DRC/LVS rules needed for dynamic cells and routing * DRC/LVS rules needed for dynamic cells and routing
* Layer information * Layer information
* Spice and supply information
* etc. * etc.
# Get Involved # Get Involved
+ Report bugs by submitting a [Github issue]. + Report bugs by submitting [Github issues].
+ Develop new features (see [how to contribute](./CONTRIBUTING.md)) + Develop new features (see [how to contribute](./CONTRIBUTING.md))
+ Submit code/fixes using a [Github pull request] + Submit code/fixes using a [Github pull request]
+ Follow our [project][Github projects]. + Follow our [project][Github projects].
+ Read and cite our [ICCAD paper][OpenRAMpaper] + Read and cite our [ICCAD paper][OpenRAMpaper]
# Further Help
+ [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])
# License # License
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE). OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
# Contributors & Acknowledgment # Contributors & Acknowledgment
- [Matthew Guthaus][Matthew Guthaus] created the OpenRAM project and is the lead architect. - [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 Grimes 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.
If I forgot to add you, please let me know!
* * * * * *
[Matthew Guthaus]: https://users.soe.ucsc.edu/~mrg [Matthew Guthaus]: https://users.soe.ucsc.edu/~mrg
[Github issues]: https://github.com/PrivateRAM/PrivateRAM/issues [James Stine]: https://ece.okstate.edu/content/stine-james-e-jr-phd
[Github pull request]: https://github.com/PrivateRAM/PrivateRAM/pulls
[Github projects]: https://github.com/PrivateRAM/PrivateRAM/projects
[email me]: mailto:mrg+openram@ucsc.edu
[VLSIDA]: https://vlsida.soe.ucsc.edu [VLSIDA]: https://vlsida.soe.ucsc.edu
[OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/ [VLSIARCH]: https://vlsiarch.ecen.okstate.edu/
[OpenRAMpaper]: https://ieeexplore.ieee.org/document/7827670/
[Github issues]: https://github.com/VLSIDA/PrivateRAM/issues
[Github pull request]: https://github.com/VLSIDA/PrivateRAM/pulls
[Github projects]: https://github.com/VLSIDA/PrivateRAM/projects
[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
[Magic]: http://opencircuitdesign.com/magic/ [Magic]: http://opencircuitdesign.com/magic/
[Netgen]: http://opencircuitdesign.com/netgen/ [Netgen]: http://opencircuitdesign.com/netgen/
[Qflow]: http://opencircuitdesign.com/qflow/history.html [Qflow]: http://opencircuitdesign.com/qflow/history.html
[Ngspice]: http://ngspice.sourceforge.net/
[OSUPDK]: https://vlsiarch.ecen.okstate.edu/flow/
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents [FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf [SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
[Ngspice]: http://ngspice.sourceforge.net/
[OpenRAMpaper]: https://ieeexplore.ieee.org/document/7827670/ [Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LTE4ODMyM2I0Mzk2ZmFiMjgwYTYyMTQ4NTgwMmUwMDhiM2E1MDViNDRjYzU1NjJhZTQxNWZjMzE3M2FlODBmZjA

View File

@ -172,5 +172,5 @@ active = contact(layer_stack=("active", "contact", "poly"))
poly = contact(layer_stack=("poly", "contact", "metal1")) poly = contact(layer_stack=("poly", "contact", "metal1"))
m1m2 = contact(layer_stack=("metal1", "via1", "metal2")) m1m2 = contact(layer_stack=("metal1", "via1", "metal2"))
m2m3 = contact(layer_stack=("metal2", "via2", "metal3")) m2m3 = contact(layer_stack=("metal2", "via2", "metal3"))
#m3m4 = contact(layer_stack=("metal3", "via3", "metal4")) m3m4 = contact(layer_stack=("metal3", "via3", "metal4"))

View File

@ -22,9 +22,7 @@ class design(hierarchy_design):
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space, self.m2_space) self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space, self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space, self.m3_space) self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space, self.m3_space)
# SCMOS doesn't have m4... self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space)
#self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space)
self.m3_pitch = self.m2_pitch
def setup_drc_constants(self): def setup_drc_constants(self):
""" These are some DRC constants used in many places in the compiler.""" """ These are some DRC constants used in many places in the compiler."""
@ -38,6 +36,8 @@ class design(hierarchy_design):
self.m2_space = drc("metal2_to_metal2") self.m2_space = drc("metal2_to_metal2")
self.m3_width = drc("minwidth_metal3") self.m3_width = drc("minwidth_metal3")
self.m3_space = drc("metal3_to_metal3") self.m3_space = drc("metal3_to_metal3")
self.m4_width = drc("minwidth_metal4")
self.m4_space = drc("metal4_to_metal4")
self.active_width = drc("minwidth_active") self.active_width = drc("minwidth_active")
self.active_space = drc("active_to_body_active") self.active_space = drc("active_to_body_active")
self.contact_width = drc("minwidth_contact") self.contact_width = drc("minwidth_contact")
@ -65,8 +65,12 @@ class design(hierarchy_design):
self.readwrite_ports = [] self.readwrite_ports = []
# These are the read/write and write-only port indices # These are the read/write and write-only port indices
self.write_ports = [] self.write_ports = []
# These are the write-only port indices.
self.writeonly_ports = []
# These are teh read/write and read-only port indice # These are teh read/write and read-only port indice
self.read_ports = [] self.read_ports = []
# These are the read-only port indices.
self.readonly_ports = []
# These are all the ports # These are all the ports
self.all_ports = list(range(total_ports)) self.all_ports = list(range(total_ports))
@ -78,9 +82,11 @@ class design(hierarchy_design):
port_number += 1 port_number += 1
for port in range(OPTS.num_w_ports): for port in range(OPTS.num_w_ports):
self.write_ports.append(port_number) self.write_ports.append(port_number)
self.writeonly_ports.append(port_number)
port_number += 1 port_number += 1
for port in range(OPTS.num_r_ports): for port in range(OPTS.num_r_ports):
self.read_ports.append(port_number) self.read_ports.append(port_number)
self.readonly_ports.append(port_number)
port_number += 1 port_number += 1
def analytical_power(self, proc, vdd, temp, load): def analytical_power(self, proc, vdd, temp, load):

View File

@ -200,18 +200,19 @@ class instance(geometry):
self.mod.gds_write_file(self.gds) self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure # now write an instance of my module/structure
new_layout.addInstance(self.gds, new_layout.addInstance(self.gds,
self.mod.name,
offsetInMicrons=self.offset, offsetInMicrons=self.offset,
mirror=self.mirror, mirror=self.mirror,
rotate=self.rotate) rotate=self.rotate)
def place(self, offset, mirror="R0", rotate=0): def place(self, offset, mirror="R0", rotate=0):
""" This updates the placement of an instance. """ """ This updates the placement of an instance. """
debug.info(3, "placing instance {}".format(self.name))
# Update the placement of an already added instance # Update the placement of an already added instance
self.offset = vector(offset).snap_to_grid() self.offset = vector(offset).snap_to_grid()
self.mirror = mirror self.mirror = mirror
self.rotate = rotate self.rotate = rotate
self.update_boundary() self.update_boundary()
debug.info(3, "placing instance {}".format(self))
def get_pin(self,name,index=-1): def get_pin(self,name,index=-1):

View File

@ -34,20 +34,24 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
# because each reference must be a unique name. # because each reference must be a unique name.
# These modules ensure unique names or have no changes if they # These modules ensure unique names or have no changes if they
# aren't unique # aren't unique
ok_list = ['ms_flop', ok_list = ['contact',
'dff',
'dff_buf',
'bitcell',
'contact',
'ptx', 'ptx',
'pbitcell',
'replica_pbitcell',
'sram', 'sram',
'hierarchical_predecode2x4', 'hierarchical_predecode2x4',
'hierarchical_predecode3x8'] 'hierarchical_predecode3x8']
if name not in hierarchy_design.name_map:
# Library cells don't change
if self.is_library_cell:
return
# Name is unique so far
elif name not in hierarchy_design.name_map:
hierarchy_design.name_map.append(name) hierarchy_design.name_map.append(name)
else: else:
# Name is in our list of exceptions (they don't change)
for ok_names in ok_list: for ok_names in ok_list:
if ok_names in self.__class__.__name__: if ok_names == self.__class__.__name__:
break break
else: else:
debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1) debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1)

View File

@ -447,6 +447,11 @@ class layout(lef.lef):
def gds_read(self): def gds_read(self):
"""Reads a GDSII file in the library and checks if it exists """Reads a GDSII file in the library and checks if it exists
Otherwise, start a new layout for dynamic generation.""" Otherwise, start a new layout for dynamic generation."""
# This must be done for netlist only mode too
if os.path.isfile(self.gds_file):
self.is_library_cell=True
if OPTS.netlist_only: if OPTS.netlist_only:
self.gds = None self.gds = None
return return
@ -454,7 +459,6 @@ class layout(lef.lef):
# open the gds file if it exists or else create a blank layout # open the gds file if it exists or else create a blank layout
if os.path.isfile(self.gds_file): if os.path.isfile(self.gds_file):
debug.info(3, "opening {}".format(self.gds_file)) debug.info(3, "opening {}".format(self.gds_file))
self.is_library_cell=True
self.gds = gdsMill.VlsiLayout(units=GDS["unit"]) self.gds = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(self.gds) reader = gdsMill.Gds2reader(self.gds)
reader.loadFromFile(self.gds_file) reader.loadFromFile(self.gds_file)

View File

@ -62,6 +62,24 @@ class pin_layout:
else: else:
return False return False
def bbox(self, pin_list):
"""
Given a list of layout pins, create a bounding box layout.
"""
(ll, ur) = self.rect
min_x = ll.x
max_x = ur.x
min_y = ll.y
max_y = ur.y
for pin in pin_list:
min_x = min(min_x, pin.ll().x)
max_x = max(max_x, pin.ur().x)
min_y = min(min_y, pin.ll().y)
max_y = max(max_y, pin.ur().y)
self.rect = [vector(min_x,min_y),vector(max_x,max_y)]
def inflate(self, spacing=None): def inflate(self, spacing=None):
""" """
Inflate the rectangle by the spacing (or other rule) Inflate the rectangle by the spacing (or other rule)
@ -325,7 +343,7 @@ class pin_layout:
(r2_ll,r2_ur) = other.rect (r2_ll,r2_ur) = other.rect
def dist(x1, y1, x2, y2): def dist(x1, y1, x2, y2):
return sqrt((x2-x1)**2 + (y2-y1)**2) return math.sqrt((x2-x1)**2 + (y2-y1)**2)
left = r2_ur.x < r1_ll.x left = r2_ur.x < r1_ll.x
right = r1_ur.x < r2_ll.x right = r1_ur.x < r2_ll.x

View File

@ -148,13 +148,13 @@ class VlsiLayout:
structureNames=[] structureNames=[]
for name in self.structures: for name in self.structures:
structureNames.append(name) structureNames.append(name)
for name in self.structures: for name in self.structures:
if(len(self.structures[name].srefs)>0): #does this structure reference any others? if(len(self.structures[name].srefs)>0): #does this structure reference any others?
for sref in self.structures[name].srefs: #go through each reference for sref in self.structures[name].srefs: #go through each reference
if sref.sName in structureNames: #and compare to our list if sref.sName in structureNames: #and compare to our list
structureNames.remove(sref.sName) structureNames.remove(sref.sName)
debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames)))
self.rootStructureName = structureNames[0] self.rootStructureName = structureNames[0]
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None, def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
@ -304,6 +304,7 @@ class VlsiLayout:
debug.info(1,"DEBUG: Structure %s Found"%StructureName) debug.info(1,"DEBUG: Structure %s Found"%StructureName)
StructureFound = True StructureFound = True
debug.check(StructureFound,"Could not find layout to instantiate {}".format(StructureName))
# If layoutToAdd is a unique object (not this), then copy hierarchy, # If layoutToAdd is a unique object (not this), then copy hierarchy,

View File

@ -61,11 +61,11 @@ class bank(design.design):
#self.add_lvs_correspondence_points() #self.add_lvs_correspondence_points()
# Remember the bank center for further placement # Remember the bank center for further placement
self.bank_center=self.offset_all_coordinates().scale(-1,-1) self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1)
self.bank_array_ur = self.bitcell_array_inst.ur()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" Adding pins for Bank module""" """ Adding pins for Bank module"""
for port in self.read_ports: for port in self.read_ports:
@ -232,8 +232,8 @@ class bank(design.design):
self.row_decoder_offsets[port] = vector(-x_offset,0) self.row_decoder_offsets[port] = vector(-x_offset,0)
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change) # Place the col decoder left aligned with row decoder (x_offset doesn't change)
# Below the bitcell array # Below the bitcell array with well spacing
if self.col_addr_size > 0: if self.col_addr_size > 0:
y_offset = self.column_decoder.height y_offset = self.column_decoder.height
else: else:
@ -290,8 +290,11 @@ class bank(design.design):
# UPPER RIGHT QUADRANT # UPPER RIGHT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change) # Place the col decoder right aligned with row decoder (x_offset doesn't change)
# Below the bitcell array # Above the bitcell array with a well spacing
y_offset = self.bitcell_array.height + self.m2_gap if self.col_addr_size > 0:
y_offset = self.bitcell_array.height + self.column_decoder.height
else:
y_offset = self.bitcell_array.height
y_offset += 2*drc("well_to_well") y_offset += 2*drc("well_to_well")
self.column_decoder_offsets[port] = vector(x_offset,y_offset) self.column_decoder_offsets[port] = vector(x_offset,y_offset)
@ -723,7 +726,7 @@ class bank(design.design):
for port in self.all_ports: for port in self.all_ports:
if port%2 == 1: if port%2 == 1:
mirror = "MY" mirror = "XY"
else: else:
mirror = "R0" mirror = "R0"
self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror) self.column_decoder_inst[port].place(offset=offsets[port], mirror=mirror)
@ -873,9 +876,9 @@ class bank(design.design):
if self.col_addr_size==0: if self.col_addr_size==0:
return return
bottom_inst = self.column_mux_array_inst[port] inst1 = self.column_mux_array_inst[port]
top_inst = self.precharge_array_inst[port] inst2 = self.precharge_array_inst[port]
self.connect_bitlines(top_inst, bottom_inst, self.num_cols) self.connect_bitlines(inst1, inst2, self.num_cols)
def route_column_mux_to_bitcell_array(self, port): def route_column_mux_to_bitcell_array(self, port):
""" Routing of BL and BR between col mux bitcell array """ """ Routing of BL and BR between col mux bitcell array """
@ -884,47 +887,50 @@ class bank(design.design):
if self.col_addr_size==0: if self.col_addr_size==0:
return return
bottom_inst = self.column_mux_array_inst[port] inst2 = self.column_mux_array_inst[port]
top_inst = self.bitcell_array_inst inst1 = self.bitcell_array_inst
self.connect_bitlines(top_inst, bottom_inst, self.num_cols) inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
def route_sense_amp_to_column_mux_or_precharge_array(self, port): def route_sense_amp_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """ """ Routing of BL and BR between sense_amp and column mux or precharge array """
bottom_inst = self.sense_amp_array_inst[port] inst2 = self.sense_amp_array_inst[port]
if self.col_addr_size>0: if self.col_addr_size>0:
# Sense amp is connected to the col mux # Sense amp is connected to the col mux
top_inst = self.column_mux_array_inst[port] inst1 = self.column_mux_array_inst[port]
top_bl = "bl_out_{}" inst1_bl_name = "bl_out_{}"
top_br = "br_out_{}" inst1_br_name = "br_out_{}"
else: else:
# Sense amp is directly connected to the precharge array # Sense amp is directly connected to the precharge array
top_inst = self.precharge_array_inst[port] inst1 = self.precharge_array_inst[port]
top_bl = "bl_{}" inst1_bl_name = "bl_{}"
top_br = "br_{}" inst1_br_name = "br_{}"
self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size, self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=top_bl, inst1_br_name=top_br) inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
def route_write_driver_to_column_mux_or_precharge_array(self, port): def route_write_driver_to_column_mux_or_bitcell_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """ """ Routing of BL and BR between sense_amp and column mux or bitcell array """
bottom_inst = self.write_driver_array_inst[port] inst2 = self.write_driver_array_inst[port]
if self.col_addr_size>0: if self.col_addr_size>0:
# Sense amp is connected to the col mux # Write driver is connected to the col mux
top_inst = self.column_mux_array_inst[port] inst1 = self.column_mux_array_inst[port]
top_bl = "bl_out_{}" inst1_bl_name = "bl_out_{}"
top_br = "br_out_{}" inst1_br_name = "br_out_{}"
else: else:
# Sense amp is directly connected to the precharge array # Write driver is directly connected to the bitcell array
top_inst = self.precharge_array_inst[port] inst1 = self.bitcell_array_inst
top_bl = "bl_{}" inst1_bl_name = self.bl_names[port]+"_{}"
top_br = "br_{}" inst1_br_name = self.br_names[port]+"_{}"
self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size, self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=top_bl, inst1_br_name=top_br) inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
def route_write_driver_to_sense_amp(self, port): def route_write_driver_to_sense_amp(self, port):
""" Routing of BL and BR between write driver and sense amp """ """ Routing of BL and BR between write driver and sense amp """
@ -939,7 +945,7 @@ class bank(design.design):
for bit in range(self.word_size): for bit in range(self.word_size):
data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit))
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit), self.add_layout_pin_rect_center(text="dout{0}_{1}".format(port,bit),
layer=data_pin.layer, layer=data_pin.layer,
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
@ -965,6 +971,37 @@ class bank(design.design):
din_name = "din{0}_{1}".format(port,row) din_name = "din{0}_{1}".format(port,row)
self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name)
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
"""
Route the bl and br of two modules using the channel router.
"""
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst1, inst1_bl_name, inst1_br_name)
(top_inst, top_bl_name, top_br_name) = (inst2, inst2_bl_name, inst2_br_name)
else:
(bottom_inst, bottom_bl_name, bottom_br_name) = (inst2, inst2_bl_name, inst2_br_name)
(top_inst, top_bl_name, top_br_name) = (inst1, inst1_bl_name, inst1_br_name)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_bl_name.format(bit), bottom_br_name.format(bit)]
top_names = [top_bl_name.format(bit), top_br_name.format(bit)]
route_map = list(zip(bottom_names, top_names))
bottom_pins = {key: bottom_inst.get_pin(key) for key in bottom_names }
top_pins = {key: top_inst.get_pin(key) for key in top_names }
all_pins = {**bottom_pins, **top_pins}
debug.check(len(all_pins)==len(bottom_pins)+len(top_pins),"Duplicate named pins in bitline channel route.")
self.create_horizontal_channel_route(route_map, all_pins, offset)
def connect_bitlines(self, inst1, inst2, num_bits, def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"): inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
@ -1189,8 +1226,8 @@ class bank(design.design):
# clk to wordline_driver # clk to wordline_driver
control_signal = self.prefix+"clk_buf{}".format(port) control_signal = self.prefix+"clk_buf{}".format(port)
pin_pos = self.wordline_driver_inst[port].get_pin("en").uc() pin_pos = self.wordline_driver_inst[port].get_pin("en").bc()
mid_pos = pin_pos + vector(0,self.m1_pitch) mid_pos = pin_pos - vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[port][control_signal].x control_x_offset = self.bus_xoffset[port][control_signal].x
control_pos = vector(control_x_offset, mid_pos.y) control_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos]) self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])

View File

@ -4,7 +4,7 @@ from tech import drc, spice
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
unique_id = 1
class bitcell_array(design.design): class bitcell_array(design.design):
""" """
@ -12,8 +12,13 @@ class bitcell_array(design.design):
and word line is connected by abutment. and word line is connected by abutment.
Connects the word lines and bit lines. Connects the word lines and bit lines.
""" """
unique_id = 1
def __init__(self, cols, rows, name="bitcell_array"): def __init__(self, cols, rows, name=""):
if name == "":
name = "bitcell_array_{0}x{1}_{2}".format(rows,cols,bitcell_array.unique_id)
bitcell_array.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))

View File

@ -45,7 +45,6 @@ class control_logic(design.design):
def create_layout(self): def create_layout(self):
""" Create layout and route between modules """ """ Create layout and route between modules """
self.route_rails()
self.place_instances() self.place_instances()
self.route_all() self.route_all()
@ -149,7 +148,7 @@ class control_logic(design.design):
def route_rails(self): def route_rails(self):
""" Add the input signal inverted tracks """ """ Add the input signal inverted tracks """
height = 4*self.inv1.height - self.m2_pitch height = self.control_logic_center.y - self.m2_pitch
offset = vector(self.ctrl_dff_array.width,0) offset = vector(self.ctrl_dff_array.width,0)
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)
@ -182,21 +181,21 @@ class control_logic(design.design):
row += 2 row += 2
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.place_we_row(row=row) self.place_we_row(row=row)
pre_height = self.w_en_inst.uy() height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.by() control_center_y = self.w_en_inst.uy()
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_in_row(row=row) self.place_rbl_in_row(row=row)
self.place_sen_row(row=row+1) self.place_sen_row(row=row+1)
self.place_rbl(row=row+2) self.place_rbl(row=row+2)
pre_height = self.rbl_inst.uy() height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by() control_center_y = self.rbl_inst.by()
# This offset is used for placement of the control logic in the SRAM level. # This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
# Extra pitch on top and right # Extra pitch on top and right
self.height = pre_height + self.m3_pitch self.height = height + 2*self.m1_pitch
# Max of modules or logic rows # Max of modules or logic rows
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
@ -205,6 +204,7 @@ class control_logic(design.design):
def route_all(self): def route_all(self):
""" Routing between modules """ """ Routing between modules """
self.route_rails()
self.route_dffs() self.route_dffs()
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen() self.route_wen()

View File

@ -12,11 +12,13 @@ class dff_buf(design.design):
with two inverters, of variable size, to provide q with two inverters, of variable size, to provide q
and qbar. This is to enable driving large fanout loads. and qbar. This is to enable driving large fanout loads.
""" """
unique_id = 1
def __init__(self, inv1_size=2, inv2_size=4, name=""): def __init__(self, inv1_size=2, inv2_size=4, name=""):
if name=="": if name=="":
name = "dff_buf_{0}_{1}".format(inv1_size, inv2_size) name = "dff_buf_{0}".format(dff_buf.unique_id)
dff_buf.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))

View File

@ -11,13 +11,15 @@ class dff_buf_array(design.design):
This is a simple row (or multiple rows) of flops. This is a simple row (or multiple rows) of flops.
Unlike the data flops, these are never spaced out. Unlike the data flops, these are never spaced out.
""" """
unique_id = 1
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""): def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
self.rows = rows self.rows = rows
self.columns = columns self.columns = columns
if name=="": if name=="":
name = "dff_buf_array_{0}x{1}".format(rows, columns) name = "dff_buf_array_{0}x{1}_{2}".format(rows, columns, dff_buf_array.unique_id)
dff_buf_array.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.inv1_size = inv1_size self.inv1_size = inv1_size

View File

@ -11,11 +11,13 @@ class dff_inv(design.design):
This is a simple DFF with an inverted output. Some DFFs This is a simple DFF with an inverted output. Some DFFs
do not have Qbar, so this will create it. do not have Qbar, so this will create it.
""" """
unique_id = 1
def __init__(self, inv_size=2, name=""): def __init__(self, inv_size=2, name=""):
if name=="": if name=="":
name = "dff_inv_{0}".format(inv_size) name = "dff_inv_{0}".format(dff_inv.unique_id)
dff_inv.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.inv_size = inv_size self.inv_size = inv_size

View File

@ -11,13 +11,15 @@ class dff_inv_array(design.design):
This is a simple row (or multiple rows) of flops. This is a simple row (or multiple rows) of flops.
Unlike the data flops, these are never spaced out. Unlike the data flops, these are never spaced out.
""" """
unique_id = 1
def __init__(self, rows, columns, inv_size=2, name=""): def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows self.rows = rows
self.columns = columns self.columns = columns
if name=="": if name=="":
name = "dff_inv_array_{0}x{1}".format(rows, columns) name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id)
dff_inv_array.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.inv_size = inv_size self.inv_size = inv_size

View File

@ -267,7 +267,7 @@ class hierarchical_predecode(design.design):
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
in_xoffset = self.in_inst[0].rx() in_xoffset = self.in_inst[0].rx()
out_xoffset = self.inv_inst[0].lx() out_xoffset = self.inv_inst[0].lx() - self.m1_space
for num in range(0,self.number_of_outputs): for num in range(0,self.number_of_outputs):
# this will result in duplicate polygons for rails, but who cares # this will result in duplicate polygons for rails, but who cares

View File

@ -90,7 +90,7 @@ class replica_bitline(design.design):
self.add_mod(self.bitcell) self.add_mod(self.bitcell)
# This is the replica bitline load column that is the height of our array # This is the replica bitline load column that is the height of our array
self.rbl = bitcell_array(name="bitline_load", cols=1, rows=self.bitcell_loads) self.rbl = bitcell_array(cols=1, rows=self.bitcell_loads)
self.add_mod(self.rbl) self.add_mod(self.rbl)
# FIXME: The FO and depth of this should be tuned # FIXME: The FO and depth of this should be tuned

View File

@ -3,6 +3,7 @@ Some utility functions for sets of grid cells.
""" """
import debug import debug
import math
from direction import direction from direction import direction
from vector3d import vector3d from vector3d import vector3d
@ -139,3 +140,16 @@ def flatten_set(curset):
else: else:
newset.update(flatten_set(c)) newset.update(flatten_set(c))
return newset return newset
def distance_set(coord, curset):
"""
Return the distance from a coordinate to any item in the set
"""
min_dist = math.inf
for c in curset:
min_dist = min(coord.euclidean_distance(c), min_dist)
return min_dist

View File

@ -465,15 +465,22 @@ class pin_group:
if pin.contained_by_any(self.enclosures): if pin.contained_by_any(self.enclosures):
continue continue
# Find a connector in the cardinal directions
# If there is overlap, but it isn't contained, these could all be None
# These could also be none if the pin is diagonal from the enclosure
left_connector = self.find_left_connector(pin, self.enclosures) left_connector = self.find_left_connector(pin, self.enclosures)
right_connector = self.find_right_connector(pin, self.enclosures) right_connector = self.find_right_connector(pin, self.enclosures)
above_connector = self.find_above_connector(pin, self.enclosures) above_connector = self.find_above_connector(pin, self.enclosures)
below_connector = self.find_below_connector(pin, self.enclosures) below_connector = self.find_below_connector(pin, self.enclosures)
for connector in [left_connector, right_connector, above_connector, below_connector]: connector_list = [left_connector, right_connector, above_connector, below_connector]
if connector: filtered_list = list(filter(lambda x: x!=None, connector_list))
self.enclosures.append(connector) if (len(filtered_list)>0):
import copy
bbox_connector = copy.copy(pin)
bbox_connector.bbox(filtered_list)
self.enclosures.append(bbox_connector)
# Now, make sure each pin touches an enclosure. If not, add a connector. # Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector.
# This could only happen when there was no enclosure in any cardinal direction from a pin # This could only happen when there was no enclosure in any cardinal direction from a pin
for pin_list in self.pins: for pin_list in self.pins:
if not self.overlap_any_shape(pin_list, self.enclosures): if not self.overlap_any_shape(pin_list, self.enclosures):
@ -495,6 +502,16 @@ class pin_group:
self.grids = pg1.grids | pg2.grids # OR the set of grid locations self.grids = pg1.grids | pg2.grids # OR the set of grid locations
self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids
def add_group(self, pg):
"""
Combine the pin group into this one. This will add to the first item in the pins
so this should be used before there are disconnected pins.
"""
debug.check(len(self.pins)==1,"Don't know which group to add pins to.")
self.pins[0].update(*pg.pins) # Join the two lists of pins
self.grids |= pg.grids # OR the set of grid locations
self.secondary_grids |= pg.secondary_grids
def add_enclosure(self, cell): def add_enclosure(self, cell):
""" """
Add the enclosure shape to the given cell. Add the enclosure shape to the given cell.
@ -585,7 +602,7 @@ class pin_group:
# At least one of the groups must have some valid tracks # At least one of the groups must have some valid tracks
if (len(pin_set)==0 and len(blockage_set)==0): if (len(pin_set)==0 and len(blockage_set)==0):
debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) #debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
for pin_list in self.pins: for pin_list in self.pins:
for pin in pin_list: for pin in pin_list:
@ -639,7 +656,6 @@ class pin_group:
that is ensured to overlap the supply rail wire. that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure. It then adds rectangle(s) for the enclosure.
""" """
additional_set = set() additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it # Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set)) e = next(iter(start_set))
@ -663,3 +679,6 @@ class pin_group:
self.set_routed() self.set_routed()
self.enclosures = self.compute_enclosures() self.enclosures = self.compute_enclosures()

View File

@ -9,9 +9,10 @@ from pin_layout import pin_layout
from pin_group import pin_group from pin_group import pin_group
from vector import vector from vector import vector
from vector3d import vector3d from vector3d import vector3d
from globals import OPTS from globals import OPTS,print_time
from pprint import pformat from pprint import pformat
import grid_utils import grid_utils
from datetime import datetime
class router(router_tech): class router(router_tech):
""" """
@ -31,6 +32,7 @@ class router(router_tech):
# If didn't specify a gds blockage file, write it out to read the gds # If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now # This isn't efficient, but easy for now
#start_time = datetime.now()
if not gds_filename: if not gds_filename:
gds_filename = OPTS.openram_temp+"temp.gds" gds_filename = OPTS.openram_temp+"temp.gds"
self.cell.gds_write(gds_filename) self.cell.gds_write(gds_filename)
@ -40,6 +42,7 @@ class router(router_tech):
self.reader = gdsMill.Gds2reader(self.layout) self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gds_filename) self.reader.loadFromFile(gds_filename)
self.top_name = self.layout.rootStructureName self.top_name = self.layout.rootStructureName
#print_time("GDS read",datetime.now(), start_time)
### The pin data structures ### The pin data structures
# A map of pin names to a set of pin_layout structures # A map of pin names to a set of pin_layout structures
@ -127,8 +130,12 @@ class router(router_tech):
Pin can either be a label or a location,layer pair: [[x,y],layer]. Pin can either be a label or a location,layer pair: [[x,y],layer].
""" """
debug.info(1,"Finding pins for {}.".format(pin_name)) debug.info(1,"Finding pins for {}.".format(pin_name))
#start_time = datetime.now()
self.retrieve_pins(pin_name) self.retrieve_pins(pin_name)
#print_time("Retrieved pins",datetime.now(), start_time)
#start_time = datetime.now()
self.analyze_pins(pin_name) self.analyze_pins(pin_name)
#print_time("Analyzed pins",datetime.now(), start_time)
def find_blockages(self): def find_blockages(self):
""" """
@ -153,96 +160,97 @@ class router(router_tech):
# This will get all shapes as blockages and convert to grid units # This will get all shapes as blockages and convert to grid units
# This ignores shapes that were pins # This ignores shapes that were pins
#start_time = datetime.now()
self.find_blockages() self.find_blockages()
#print_time("Find blockags",datetime.now(), start_time)
# Convert the blockages to grid units # Convert the blockages to grid units
#start_time = datetime.now()
self.convert_blockages() self.convert_blockages()
#print_time("Find blockags",datetime.now(), start_time)
# This will convert the pins to grid units # This will convert the pins to grid units
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids # It must be done after blockages to ensure no DRCs between expanded pins and blocked grids
#start_time = datetime.now()
for pin in pin_list: for pin in pin_list:
self.convert_pins(pin) self.convert_pins(pin)
#print_time("Convert pins",datetime.now(), start_time)
#start_time = datetime.now()
for pin in pin_list: for pin in pin_list:
self.combine_adjacent_pins(pin) self.combine_adjacent_pins(pin)
#print_time("Combine pins",datetime.now(), start_time)
#self.write_debug_gds("debug_combine_pins.gds",stop_program=True) #self.write_debug_gds("debug_combine_pins.gds",stop_program=True)
# Separate any adjacent grids of differing net names to prevent wide metal DRC violations # Separate any adjacent grids of differing net names to prevent wide metal DRC violations
# Must be done before enclosing pins # Must be done before enclosing pins
#start_time = datetime.now()
self.separate_adjacent_pins(self.supply_rail_space_width) self.separate_adjacent_pins(self.supply_rail_space_width)
#print_time("Separate pins",datetime.now(), start_time)
# For debug # For debug
#self.separate_adjacent_pins(1) #self.separate_adjacent_pins(1)
# Enclose the continguous grid units in a metal rectangle to fix some DRCs # Enclose the continguous grid units in a metal rectangle to fix some DRCs
#start_time = datetime.now()
self.enclose_pins() self.enclose_pins()
#print_time("Enclose pins",datetime.now(), start_time)
#self.write_debug_gds("debug_enclose_pins.gds",stop_program=True) #self.write_debug_gds("debug_enclose_pins.gds",stop_program=True)
def combine_adjacent_pins_pass(self, pin_name): def combine_adjacent_pins(self, pin_name):
""" """
Find pins that have adjacent routing tracks and merge them into a Find pins that have adjacent routing tracks and merge them into a
single pin_group. The pins themselves may not be touching, but single pin_group. The pins themselves may not be touching, but
enclose_pis in the next step will ensure they are touching. enclose_pis in the next step will ensure they are touching.
""" """
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# Make a copy since we are going to add to (and then reduce) this list # Find all adjacencies
pin_groups = self.pin_groups[pin_name].copy() adjacent_pins = {}
# Start as None to signal the first iteration
remove_indices = set()
for index1,pg1 in enumerate(self.pin_groups[pin_name]): for index1,pg1 in enumerate(self.pin_groups[pin_name]):
# Cannot combine more than once
if index1 in remove_indices:
continue
for index2,pg2 in enumerate(self.pin_groups[pin_name]): for index2,pg2 in enumerate(self.pin_groups[pin_name]):
# Cannot combine with yourself # Cannot combine with yourself, also don't repeat
if index1==index2: if index1<=index2:
continue continue
# Cannot combine more than once
if index2 in remove_indices:
continue
# Combine if at least 1 grid cell is adjacent # Combine if at least 1 grid cell is adjacent
if pg1.adjacent(pg2): if pg1.adjacent(pg2):
combined = pin_group(pin_name, [], self) if not index1 in adjacent_pins.keys():
combined.combine_groups(pg1, pg2) adjacent_pins[index1] = set([index2])
debug.info(3,"Combining {0} {1} {2}:".format(pin_name, index1, index2)) else:
debug.info(3, " {0}\n {1}".format(pg1.pins, pg2.pins)) adjacent_pins[index1].add(index2)
# Make a list of indices to ensure every group gets in the new set
all_indices = set([x for x in range(len(self.pin_groups[pin_name]))])
# Now reconstruct the new groups
new_pin_groups = []
for index1,index2_set in adjacent_pins.items():
# Remove the indices if they are added to the new set
all_indices.discard(index1)
all_indices.difference_update(index2_set)
# Create the combined group starting with the first item
combined = self.pin_groups[pin_name][index1]
# Add all of the other items that overlapped
for index2 in index2_set:
pg = self.pin_groups[pin_name][index2]
combined.add_group(pg)
debug.info(3,"Combining {0} {1}:".format(pin_name, index2))
debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins))
debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids))
remove_indices.update([index1,index2]) new_pin_groups.append(combined)
pin_groups.append(combined)
break
# Remove them in decreasing order to not invalidate the indices # Add the pin groups that weren't added to the new set
debug.info(4,"Removing {}".format(sorted(remove_indices))) for index in all_indices:
for i in sorted(remove_indices, reverse=True): new_pin_groups.append(self.pin_groups[pin_name][index])
del pin_groups[i]
old_size = len(self.pin_groups[pin_name])
# Use the new pin group! # Use the new pin group!
self.pin_groups[pin_name] = pin_groups self.pin_groups[pin_name] = new_pin_groups
removed_pairs = old_size - len(new_pin_groups)
removed_pairs = int(len(remove_indices)/2) debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name))
debug.info(1, "Combined {0} pin pairs for {1}".format(removed_pairs,pin_name))
return removed_pairs return removed_pairs
def combine_adjacent_pins(self, pin_name):
"""
Make multiple passes of the combine adjacent pins until we have no
more combinations or hit an iteration limit.
"""
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# Start as None to signal the first iteration
num_removed_pairs = None
# Just used in case there's a circular combination or something weird
for iteration_count in range(10):
num_removed_pairs = self.combine_adjacent_pins_pass(pin_name)
if num_removed_pairs==0:
break
else:
debug.warning("Did not converge combining adjacent pins in supply router.")
def separate_adjacent_pins(self, separation): def separate_adjacent_pins(self, separation):
""" """
@ -271,7 +279,7 @@ class router(router_tech):
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
for index1,pg1 in enumerate(self.pin_groups[pin_name1]): for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
for index2,pg2 in enumerate(self.pin_groups[pin_name2]): for index2,pg2 in enumerate(self.pin_groups[pin_name2]):
# FIXME: Use separation distance and edge grids only # FIgXME: Use separation distance and edge grids only
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation) grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
# These should have the same length, so... # These should have the same length, so...
if len(grids_g1)>0: if len(grids_g1)>0:
@ -507,7 +515,7 @@ class router(router_tech):
# scale the size bigger to include neaby tracks # scale the size bigger to include neaby tracks
ll=ll.scale(self.track_factor).floor() ll=ll.scale(self.track_factor).floor()
ur=ur.scale(self.track_factor).ceil() ur=ur.scale(self.track_factor).ceil()
#print(pin)
# Keep tabs on tracks with sufficient and insufficient overlap # Keep tabs on tracks with sufficient and insufficient overlap
sufficient_list = set() sufficient_list = set()
insufficient_list = set() insufficient_list = set()
@ -515,35 +523,50 @@ class router(router_tech):
zindex=self.get_zindex(pin.layer_num) zindex=self.get_zindex(pin.layer_num)
for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion): for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion):
for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion): for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion):
debug.info(4,"Converting [ {0} , {1} ]".format(x,y))
(full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) (full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
if full_overlap: if full_overlap:
sufficient_list.update([full_overlap]) sufficient_list.update([full_overlap])
if partial_overlap: if partial_overlap:
insufficient_list.update([partial_overlap]) insufficient_list.update([partial_overlap])
debug.info(4,"Converting [ {0} , {1} ] full={2} partial={3}".format(x,y, full_overlap, partial_overlap))
# Remove the blocked grids
sufficient_list.difference_update(self.blocked_grids)
insufficient_list.difference_update(self.blocked_grids)
if len(sufficient_list)>0: if len(sufficient_list)>0:
return sufficient_list return sufficient_list
elif expansion==0 and len(insufficient_list)>0: elif expansion==0 and len(insufficient_list)>0:
#Remove blockages and return the best to be patched best_pin = self.get_all_offgrid_pin(pin, insufficient_list)
insufficient_list.difference_update(self.blocked_grids) #print(best_pin)
return self.get_best_offgrid_pin(pin, insufficient_list) return best_pin
elif expansion>0: elif expansion>0:
#Remove blockages and return the nearest nearest_pin = self.get_furthest_offgrid_pin(pin, insufficient_list)
insufficient_list.difference_update(self.blocked_grids) return nearest_pin
return self.get_nearest_offgrid_pin(pin, insufficient_list)
else: else:
debug.error("Unable to find any overlapping grids.", -1) return set()
def get_all_offgrid_pin(self, pin, insufficient_list):
"""
Find a list of all pins with some overlap.
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
any_overlap = set()
for coord in insufficient_list:
full_pin = self.convert_track_to_pin(coord)
# Compute the overlap with that rectangle
overlap_rect=pin.compute_overlap(full_pin)
# Determine the max x or y overlap
max_overlap = max(overlap_rect)
if max_overlap>0:
any_overlap.update([coord])
return any_overlap
def get_best_offgrid_pin(self, pin, insufficient_list): def get_best_offgrid_pin(self, pin, insufficient_list):
""" """
Given a pin and a list of partial overlap grids: Find a list of the single pin with the most overlap.
1) Find the unblocked grids.
2) If one, use it.
3) If not, find the greatest overlap.
4) Add a pin with the most overlap to make it "on grid"
that is not blocked.
""" """
#print("INSUFFICIENT LIST",insufficient_list) #print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap # Find the coordinate with the most overlap
@ -561,6 +584,23 @@ class router(router_tech):
return set([best_coord]) return set([best_coord])
def get_furthest_offgrid_pin(self, pin, insufficient_list):
"""
Get a grid cell that is the furthest from the blocked grids.
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
best_coord = None
best_dist = math.inf
for coord in insufficient_list:
min_dist = grid_utils.distance_set(coord, self.blocked_grids)
if min_dist<best_dist:
best_dist=min_dist
best_coord=coord
return set([best_coord])
def get_nearest_offgrid_pin(self, pin, insufficient_list): def get_nearest_offgrid_pin(self, pin, insufficient_list):
""" """
Given a pin and a list of grid cells (probably non-overlapping), Given a pin and a list of grid cells (probably non-overlapping),

View File

@ -2,13 +2,14 @@ import gdsMill
import tech import tech
import math import math
import debug import debug
from globals import OPTS from globals import OPTS,print_time
from contact import contact from contact import contact
from pin_group import pin_group from pin_group import pin_group
from pin_layout import pin_layout from pin_layout import pin_layout
from vector3d import vector3d from vector3d import vector3d
from router import router from router import router
from direction import direction from direction import direction
from datetime import datetime
import grid import grid
import grid_utils import grid_utils
@ -68,10 +69,13 @@ class supply_router(router):
self.compute_supply_rail_dimensions() self.compute_supply_rail_dimensions()
# Get the pin shapes # Get the pin shapes
#start_time = datetime.now()
self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
#print_time("Pins and blockages",datetime.now(), start_time)
#self.write_debug_gds("pin_enclosures.gds",stop_program=True) #self.write_debug_gds("pin_enclosures.gds",stop_program=True)
# Add the supply rails in a mesh network and connect H/V with vias # Add the supply rails in a mesh network and connect H/V with vias
#start_time = datetime.now()
# Block everything # Block everything
self.prepare_blockages(self.gnd_name) self.prepare_blockages(self.gnd_name)
# Determine the rail locations # Determine the rail locations
@ -82,15 +86,20 @@ class supply_router(router):
# Determine the rail locations # Determine the rail locations
self.route_supply_rails(self.vdd_name,1) self.route_supply_rails(self.vdd_name,1)
#self.write_debug_gds("debug_rails.gds",stop_program=True) #self.write_debug_gds("debug_rails.gds",stop_program=True)
#print_time("Supply rails",datetime.now(), start_time)
#start_time = datetime.now()
self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(vdd_name)
self.route_simple_overlaps(gnd_name) self.route_simple_overlaps(gnd_name)
#print_time("Simple overlaps",datetime.now(), start_time)
#self.write_debug_gds("debug_simple_route.gds",stop_program=False) #self.write_debug_gds("debug_simple_route.gds",stop_program=False)
# Route the supply pins to the supply rails # Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter # Route vdd first since we want it to be shorter
#start_time = datetime.now()
self.route_pins_to_rails(vdd_name) self.route_pins_to_rails(vdd_name)
self.route_pins_to_rails(gnd_name) self.route_pins_to_rails(gnd_name)
#print_time("Routing",datetime.now(), start_time)
#self.write_debug_gds("debug_pin_routes.gds",stop_program=True) #self.write_debug_gds("debug_pin_routes.gds",stop_program=True)
#self.write_debug_gds("final.gds",False) #self.write_debug_gds("final.gds",False)

View File

@ -164,9 +164,13 @@ class vector3d():
return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z)) return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z))
def distance(self, other): def distance(self, other):
""" Return the planar distance between two values """ """ Return the manhattan distance between two values """
return abs(self.x-other.x)+abs(self.y-other.y) return abs(self.x-other.x)+abs(self.y-other.y)
def euclidean_distance(self, other):
""" Return the euclidean distance between two values """
return math.sqrt((self.x-other.x)**2+(self.y-other.y)**2)
def adjacent(self, other): def adjacent(self, other):
""" Is the one grid adjacent in any planar direction to the other """ """ Is the one grid adjacent in any planar direction to the other """

View File

@ -43,10 +43,10 @@ class sram_1bank(sram_base):
self.data_dff_insts = self.create_data_dff() self.data_dff_insts = self.create_data_dff()
def place_modules(self): def place_instances(self):
""" """
This places the modules for a single bank SRAM with control This places the instances for a single bank SRAM with control
logic. logic and up to 2 ports.
""" """
# No orientation or offset # No orientation or offset
@ -57,38 +57,76 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array) # the sense amps/column mux and cell array)
# 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: control_pos = [None]*len(self.all_ports)
control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch, row_addr_pos = [None]*len(self.all_ports)
self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y) col_addr_pos = [None]*len(self.all_ports)
self.control_logic_insts[port].place(control_pos) data_pos = [None]*len(self.all_ports)
# The row address bits are placed above the control logic aligned on the right.
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 # 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)
# Add the column address below the bank under the control # Port 0
# The column address flops are aligned with the data flops port = 0
# This includes 2 M2 pitches for the row addr clock line
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port])
# The row address bits are placed above the control logic aligned on the right.
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
# It is aove the control logic but below the top of the bitcell array
y_offset = max(self.control_logic_insts[port].uy(), self.bank.bank_array_ur.y - self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port])
# Add the col address flops below the bank to the left of the lower-left of bank array
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[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width,
data_gap - self.col_addr_dff.height) -data_gap - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos) self.col_addr_dff_insts[port].place(col_addr_pos[port])
# 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 lower-left of bank array
# This relies on the center point of the bank: # This relies on the lower-left of the array of the bank
# decoder in upper left, bank in upper right, sensing in lower right. # decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the # These flops go below the sensing and leave a gap to channel route to the
# sense amps. # sense amps.
data_pos = vector(self.bank.bank_center.x, if port in self.write_ports:
data_gap - self.data_dff.height) data_pos[port] = vector(self.bank.bank_array_ll.x,
self.data_dff_insts[port].place(data_pos) -data_gap - self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port])
if len(self.all_ports)>1:
# Port 1
port = 1
# This includes 2 M2 pitches for the row addr clock line
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port], mirror="MY")
# The row address bits are placed above the control logic aligned on the left.
x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# It is above the control logic but below the top of the bitcell array
y_offset = max(self.control_logic_insts[port].uy(), self.bank.bank_array_ur.y - self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="MY")
# Add the col address flops above the bank to the right of the upper-right of bank array
if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width,
self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
# Add the data flops above the bank to the left of the upper-right of bank array
# This relies on the upper-right of the array of the bank
# decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.uy() + data_gap + self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
# 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.height = self.bank.height
def add_layout_pins(self): def add_layout_pins(self):
""" """
@ -114,7 +152,7 @@ class sram_1bank(sram_base):
for bit in range(self.word_size): for bit in range(self.word_size):
self.copy_layout_pin(self.data_dff_insts[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_layout(self):
""" Route a single bank SRAM """ """ Route a single bank SRAM """
self.add_layout_pins() self.add_layout_pins()
@ -152,16 +190,23 @@ class sram_1bank(sram_base):
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])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[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 (for port0) of the control/row addr DFF
# to route vertically. # to route vertically. For port1, it is to the left.
control_clk_buf_pin = self.control_logic_insts[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_insts[port].get_pin("clk") row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
if port%2:
control_clk_buf_pos = control_clk_buf_pin.lc()
row_addr_clk_pos = row_addr_clk_pin.lc()
mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch,
row_addr_clk_pos.y)
else:
control_clk_buf_pos = control_clk_buf_pin.rc()
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_insts[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)

View File

@ -75,8 +75,9 @@ class sram_base(design):
def create_layout(self): def create_layout(self):
""" Layout creation """ """ Layout creation """
self.place_modules() self.place_instances()
self.route()
self.route_layout()
self.add_lvs_correspondence_points() self.add_lvs_correspondence_points()
@ -225,12 +226,12 @@ class sram_base(design):
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
port_type="rw") port_type="rw")
self.add_mod(self.control_logic_rw) self.add_mod(self.control_logic_rw)
if len(self.write_ports)>0: if len(self.writeonly_ports)>0:
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
port_type="w") port_type="w")
self.add_mod(self.control_logic_w) self.add_mod(self.control_logic_w)
if len(self.read_ports)>0: if len(self.readonly_ports)>0:
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
port_type="r") port_type="r")
@ -369,9 +370,13 @@ class sram_base(design):
def create_data_dff(self): def create_data_dff(self):
""" Add and place all data flops """ """ Add and place all data flops """
insts = [] insts = []
for port in self.write_ports: for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="data_dff{}".format(port), insts.append(self.add_inst(name="data_dff{}".format(port),
mod=self.data_dff)) mod=self.data_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar # inputs, outputs/output/bar
inputs = [] inputs = []

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
Run a test on a multiport replica bitline
"""
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 replica_bitline_multiport_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import replica_bitline
stages=4
fanout=4
rows=13
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
debug.info(2, "Testing 1rw 1r RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
# check replica bitline in pbitcell multi-port
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
debug.info(2, "Testing RBL pbitcell 1rw with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
debug.info(2, "Testing RBL pbitcell 1rw 1w 1r with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
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()

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Run a test on a delay chain Run a test on a replica bitline
""" """
import unittest import unittest
@ -32,61 +32,6 @@ class replica_bitline_test(openram_test):
a = replica_bitline.replica_bitline(stages,fanout,rows) a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a) self.local_check(a)
#check replica bitline in handmade multi-port 1rw+1r cell
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
stages=4
fanout=4
rows=13
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
# check replica bitline in pbitcell multi-port
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 0
stages=4
fanout=4
rows=13
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
stages=4
fanout=4
rows=13
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
stages=8
rows=100
debug.info(2, "Testing RBL with {0} FO4 stages, {1} rows".format(stages,rows))
a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
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
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class psram_1bank_2mux_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 0
OPTS.num_w_ports = 1
OPTS.num_r_ports = 1
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux 1w/1r with control logic")
a = sram(c, "sram")
self.local_check(a, final_verification=True)
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()

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank, 2 port SRAM
"""
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 sram_1bank_2mux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.words_per_row=2
debug.info(1, "Single bank, two way column mux 1rw, 1r with control logic")
a = sram(c, "sram")
self.local_check(a, final_verification=True)
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()

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank, 2 port SRAM
"""
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 sram_1bank_nomux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
c = sram_config(word_size=4,
num_words=16,
num_banks=1)
c.words_per_row=1
debug.info(1, "Single bank, no column mux 1rw, 1r with control logic")
a = sram(c, "sram")
self.local_check(a, final_verification=True)
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.

View File

@ -1,568 +1,113 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) --> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="0 0 465.75 458.25" width="465.75pt" height="458.25pt" xmlns:dc="http://purl.org/dc/elements/1.1/">
<svg <metadata> Produced by OmniGraffle 7.6.1
xmlns:dc="http://purl.org/dc/elements/1.1/" <dc:date>2018-11-16 00:52:28 +0000</dc:date>
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="sense_amp_schem.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4142136"
inkscape:cx="78.947962"
inkscape:cy="503.38478"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="3440"
inkscape:window-height="1392"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2989" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata> </metadata>
<g <defs>
inkscape:label="Layer 1" <font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.68359" underline-thickness="49.316406" slope="0" x-height="522.9492" cap-height="717.28516" ascent="770.0195" descent="-229.98047" font-weight="500">
inkscape:groupmode="layer" <font-face-src>
id="layer1"> <font-face-name name="Helvetica"/>
<g </font-face-src>
id="g3496-2" </font-face>
transform="matrix(0,-1,1,0,-62.34811,791.36093)"> <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledCenterBall_Marker" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="black">
<g <g>
transform="matrix(1.25,0,0,1.25,240,223.19311)" <circle cx="2.9999986" cy="0" r="2.999997" fill="currentColor" stroke="currentColor" stroke-width="1"/>
id="g3011-2">
<path
id="path3013-5"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 31.199,119.324 29.539,0 0,-14.66 36.922,0 0,14.66 29.539,0"
inkscape:connector-curvature="0" />
</g> </g>
<g </marker>
transform="matrix(1.25,0,0,1.25,240,223.19311)" <marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledCenterBall_Marker_2" viewBox="-7 -4 8 8" markerWidth="8" markerHeight="8" color="black">
id="g3015-0"> <g>
<path <circle cx="-2.9999986" cy="0" r="2.999997" fill="currentColor" stroke="currentColor" stroke-width="1"/>
id="path3017-5"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 61.09,100.621 36.57,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3019-3">
<path
id="path3021-5"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 58.523,104.664 40.614,0"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g3496-3"
transform="matrix(0,1,-1,0,802.34811,113.36343)">
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3011-6">
<path
id="path3013-6"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 31.199,119.324 29.539,0 0,-14.66 36.922,0 0,14.66 29.539,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3015-2">
<path
id="path3017-1"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 61.09,100.621 36.57,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3019-35">
<path
id="path3021-51"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 58.523,104.664 40.614,0"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g3496-8"
transform="matrix(0,-1,1,0,-2.34811,920.37376)">
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3011-0">
<path
id="path3013-66"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 31.199,119.324 29.539,0 0,-14.66 36.922,0 0,14.66 29.539,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3015-3">
<path
id="path3017-52"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 61.09,100.621 36.57,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3019-1">
<path
id="path3021-6"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 58.523,104.664 40.614,0"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g3630"
transform="matrix(0,-1,1,0,-332.3481,1091.3609)">
<g
transform="matrix(1.25,0,0,1.25,370,363.1931)"
id="g3035">
<path
id="path3037"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 31.199,271.324 -16,0 0,-16 0,32"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,370,363.1931)"
id="g3039">
<path
id="path3041"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 7.199,263.324 0,16"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1.25,0,0,1.25,370,363.1931)"
id="g3043">
<path
id="path3045"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 0.801,266.523 0,9.602"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g3689"
transform="translate(1.00125,0.01406962)">
<g
transform="matrix(1.25,0,0,1.25,-200,23.193113)"
id="g3223">
<path
id="path3225"
style="fill:none;stroke:#000000;stroke-width:1.30079997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 434.398,221.727 c 0,2.207 -1.789,4 -4,4 -2.207,0 -4,-1.793 -4,-4 0,-2.211 1.793,-4 4,-4 2.211,0 4,1.789 4,4 z"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g3227">
<path
id="path3229"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 175.324,-407.199 29.539,0 0,-14.66 36.922,0 0,14.66 29.539,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g3231">
<path
id="path3233"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 205.215,-425.902 36.57,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g3235">
<path
id="path3237"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 202.648,-421.859 40.614,0"
inkscape:connector-curvature="0" />
</g>
</g>
<g
id="g3689-1"
transform="matrix(-1,0,0,1,738.99875,0.01406962)">
<g
transform="matrix(1.25,0,0,1.25,-200,23.193113)"
id="g3223-4">
<path
id="path3225-0"
style="fill:none;stroke:#000000;stroke-width:1.30079997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 434.398,221.727 c 0,2.207 -1.789,4 -4,4 -2.207,0 -4,-1.793 -4,-4 0,-2.211 1.793,-4 4,-4 2.211,0 4,1.789 4,4 z"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g3227-8">
<path
id="path3229-5"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 175.324,-407.199 29.539,0 0,-14.66 36.922,0 0,14.66 29.539,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g3231-3">
<path
id="path3233-9"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 205.215,-425.902 36.57,0"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g3235-8">
<path
id="path3237-5"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 202.648,-421.859 40.614,0"
inkscape:connector-curvature="0" />
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 310,362.36218 0,30"
id="path3810"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 430,362.36218 0,30"
id="path4580"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 310,242.36218 0,-20 120,0 0,20"
id="path4582"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 370,222.36218 0,-40 30,0 -60,0"
id="path4584"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 310,512.36218 120,0 -60,0 0,10"
id="path4588"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2.23606801;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 370,632.36218 0,50"
id="path4590"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 130,452.36218 -30,0 0,-120 0,220 0,10"
id="path4592"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 604,452.36218 h 30 v -120 230"
id="path4594"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 184,416.36218 v -20 -10"
id="path4598"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 544,416.36218 v -30"
id="path4600"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 490,452.36218 -40,0"
id="path4602"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 250,452.36218 40,0"
id="path4604"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 350,582.36218 -30,0"
id="path4606"
inkscape:connector-curvature="0" />
<g
id="g3319-2"
transform="matrix(1.25,0,0,1,-140,173.20718)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3321-6" />
</g>
<g
id="g3319-9"
transform="matrix(1.25,0,0,1,-410,113.20718)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3321-1" />
</g>
<g
id="g3319-3"
transform="matrix(1.25,0,0,1,125,112.20718)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3321-5" />
</g>
<g
id="g3319"
transform="matrix(1.25,0,0,1,-139,-116.79282)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3321" />
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 370,302.36218 0,70 -60,0"
id="path4656"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 430,372.36218 80,0 0,-70 0,0 -10,10 10,-10 10,10"
id="path4658"
inkscape:connector-curvature="0" />
<g
id="g3319-4"
transform="matrix(1.25,0,0,1,-201,33.207183)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3321-58" />
</g>
<g
id="g3319-91"
transform="matrix(1.25,0,0,1,-79,33.207183)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3321-4" />
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="m 345,300.36218 50,0"
id="path4680"
inkscape:connector-curvature="0" />
<g
id="g3319-1"
transform="matrix(1.25,0,0,1,-140,-37.792817)">
<path
inkscape:connector-curvature="0"
d="m 409.934,339.155 c 0,1.338 -0.864,2.417 -1.934,2.417 -1.07,0 -1.934,-1.079 -1.934,-2.417 0,-1.333 0.864,-2.417 1.934,-2.417 1.07,0 1.934,1.084 1.934,2.417 z"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.93475199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3321-42" />
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="390"
y="172.36218"
id="text4691"><tspan
sodipodi:role="line"
id="tspan4693"
x="390"
y="172.36218"
style="font-size:16px;line-height:1.25;font-family:sans-serif">vdd</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="488"
y="297.36218"
id="text4695"><tspan
sodipodi:role="line"
id="tspan4697"
x="488"
y="297.36218"
style="font-size:16px;line-height:1.25;font-family:sans-serif">DATA</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="621"
y="577.36218"
id="text4699"><tspan
sodipodi:role="line"
id="tspan4701"
x="621"
y="577.36218"
style="font-size:16px;line-height:1.25;font-family:sans-serif">br</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="89"
y="580.36218"
id="text4703"><tspan
sodipodi:role="line"
id="tspan4705"
x="89"
y="580.36218"
style="font-size:16px;line-height:1.25;font-family:sans-serif">bl</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="295"
y="588.36218"
id="text4707"><tspan
sodipodi:role="line"
id="tspan4709"
x="295"
y="588.36218"
style="font-size:16px;line-height:1.25;font-family:sans-serif">en</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="538"
y="382.36218"
id="text4711"><tspan
sodipodi:role="line"
id="tspan4713"
x="538"
y="382.36218"
style="font-size:16px;line-height:1.25;font-family:sans-serif">en</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
x="177.87868"
y="376.02765"
id="text4715"><tspan
sodipodi:role="line"
id="tspan4717"
x="177.87868"
y="376.02765"
style="font-size:16px;line-height:1.25;font-family:sans-serif">en</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0"
d="m 680,572.36218 0,-440 -620,0 0,610 620,0 z"
id="path4738"
inkscape:connector-curvature="0" />
<g
transform="rotate(-90,322.13078,437.55515)"
id="g161">
<g
id="g147"
transform="matrix(1.25,0,0,1.25,-200,23.193113)">
<path
inkscape:connector-curvature="0"
d="m 434.398,221.727 c 0,2.207 -1.789,4 -4,4 -2.207,0 -4,-1.793 -4,-4 0,-2.211 1.793,-4 4,-4 2.211,0 4,1.789 4,4 z"
style="fill:none;stroke:#000000;stroke-width:1.30079997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path145" />
</g>
<g
id="g151"
transform="matrix(0,1.25,-1.25,0,-200,23.193113)">
<path
inkscape:connector-curvature="0"
d="m 175.324,-407.199 h 29.539 v -14.66 h 36.922 v 14.66 h 29.539"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path149" />
</g>
<g
id="g155"
transform="matrix(0,1.25,-1.25,0,-200,23.193113)">
<path
inkscape:connector-curvature="0"
d="m 205.215,-425.902 h 36.57"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path153" />
</g>
<g
id="g159"
transform="matrix(0,1.25,-1.25,0,-200,23.193113)">
<path
inkscape:connector-curvature="0"
d="m 202.648,-421.859 h 40.614"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path157" />
</g>
</g>
<g
id="g179"
transform="rotate(-90,502.65766,258.08202)">
<g
transform="matrix(1.25,0,0,1.25,-200,23.193113)"
id="g165">
<path
id="path163"
style="fill:none;stroke:#000000;stroke-width:1.30079997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 434.398,221.727 c 0,2.207 -1.789,4 -4,4 -2.207,0 -4,-1.793 -4,-4 0,-2.211 1.793,-4 4,-4 2.211,0 4,1.789 4,4 z"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g169">
<path
id="path167"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 175.324,-407.199 h 29.539 v -14.66 h 36.922 v 14.66 h 29.539"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g173">
<path
id="path171"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 205.215,-425.902 h 36.57"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(0,1.25,-1.25,0,-200,23.193113)"
id="g177">
<path
id="path175"
style="fill:none;stroke:#000000;stroke-width:1.60000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 202.648,-421.859 h 40.614"
inkscape:connector-curvature="0" />
</g> </g>
</marker>
</defs>
<g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1">
<title>Canvas 1</title>
<rect fill="white" width="465.75" height="458.25"/>
<g>
<title>Layer 1</title>
<path d="M 187.875 285.375 L 187.875 257.684 L 174.133 257.684 L 174.133 223.066 L 187.875 223.066 L 187.875 195.375" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="170.34" y1="257.352" x2="170.34" y2="223.066" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="174.133" y1="259.758" x2="174.133" y2="221.684" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 277.875 195.375 L 277.875 223.066 L 291.617 223.066 L 291.617 257.684 L 277.875 257.684 L 277.875 285.375" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="295.41" y1="223.398" x2="295.41" y2="257.684" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="291.617" y1="220.992" x2="291.617" y2="259.066" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 232.875 382.133 L 232.875 354.441 L 219.133 354.441 L 219.133 319.828 L 232.875 319.828 L 232.875 292.133" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="215.34" y1="354.113" x2="215.34" y2="319.828" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="219.133" y1="356.52" x2="219.133" y2="318.441" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 232.875 412.875 L 232.875 427.875 L 217.875 427.875 L 247.875 427.875" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="225.375" y1="435.375" x2="240.375" y2="435.375" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<line x1="228.375" y1="441.375" x2="237.375" y2="441.375" stroke="black" stroke-linecap="butt" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 161.391 126.7792 C 161.391 128.8452 163.071 130.5292 165.141 130.5292 C 167.211 130.5292 168.891 128.8452 168.891 126.7792 C 168.891 124.7052 167.211 123.0292 165.141 123.0292 C 163.071 123.0292 161.391 124.7052 161.391 126.7792 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 188.188 82.1252 L 188.188 109.8162 L 174.446 109.8162 L 174.446 144.4342 L 188.188 144.4342 L 188.188 172.1252" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="169.754" y1="108.0882" x2="169.754" y2="146.1622" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 300.125 128.041 C 300.125 130.107 301.805 131.791 303.875 131.791 C 305.945 131.791 307.625 130.107 307.625 128.041 C 307.625 125.967 305.945 124.291 303.875 124.291 C 301.805 124.291 300.125 125.967 300.125 128.041 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 277.562 83.041 L 277.562 110.732 L 291.304 110.732 L 291.304 145.35 L 277.562 145.35 L 277.562 173.041" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="299.125" y1="108.0882" x2="299.125" y2="146.1622" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="187.875" y1="172.875" x2="187.875" y2="195.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="277.875" y1="172.875" x2="277.875" y2="195.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 187.875 82.875 L 187.875 67.875 L 277.875 67.875 L 277.875 82.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 232.875 67.875 L 232.875 37.875 L 255.375 37.875 L 210.375 37.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 187.875 285.375 L 277.875 285.375 L 232.875 285.375 L 232.875 292.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="232.875" y1="375.375" x2="232.875" y2="412.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 52.875 240.375 L 30.375 240.375 L 30.375 150.375 L 30.375 322.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 408.375 240.375 L 430.875 240.375 L 430.875 150.375 L 430.875 322.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="93.375" y1="213.375" x2="93.375" y2="190.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="363.375" y1="213.375" x2="363.375" y2="190.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="322.875" y1="240.375" x2="292.875" y2="240.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="142.875" y1="240.375" x2="172.875" y2="240.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="217.875" y1="337.875" x2="195.375" y2="337.875" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 234.688 285.3752 C 234.688 286.3792 233.879 287.1872 232.875 287.1872 C 231.871 287.1872 231.062 286.3792 231.062 285.3752 C 231.062 284.3752 231.871 283.5624 232.875 283.5624 C 233.879 283.5624 234.688 284.3752 234.688 285.3752 Z" fill="black"/>
<path d="M 234.688 285.3752 C 234.688 286.3792 233.879 287.1872 232.875 287.1872 C 231.871 287.1872 231.062 286.3792 231.062 285.3752 C 231.062 284.3752 231.871 283.5624 232.875 283.5624 C 233.879 283.5624 234.688 284.3752 234.688 285.3752 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 32.188 240.3752 C 32.188 241.3792 31.379 242.1872 30.375 242.1872 C 29.371 242.1872 28.562 241.3792 28.562 240.3752 C 28.562 239.3752 29.371 238.5624 30.375 238.5624 C 31.379 238.5624 32.188 239.3752 32.188 240.3752 Z" fill="black"/>
<path d="M 32.188 240.3752 C 32.188 241.3792 31.379 242.1872 30.375 242.1872 C 29.371 242.1872 28.562 241.3792 28.562 240.3752 C 28.562 239.3752 29.371 238.5624 30.375 238.5624 C 31.379 238.5624 32.188 239.3752 32.188 240.3752 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 433.438 239.6248 C 433.438 240.6288 432.629 241.4376 431.625 241.4376 C 430.621 241.4376 429.812 240.6288 429.812 239.6248 C 429.812 238.6248 430.621 237.8128 431.625 237.8128 C 432.629 237.8128 433.438 238.6248 433.438 239.6248 Z" fill="black"/>
<path d="M 433.438 239.6248 C 433.438 240.6288 432.629 241.4376 431.625 241.4376 C 430.621 241.4376 429.812 240.6288 429.812 239.6248 C 429.812 238.6248 430.621 237.8128 431.625 237.8128 C 432.629 237.8128 433.438 238.6248 433.438 239.6248 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 235.438 67.8752 C 235.438 68.8792 234.629 69.6872 233.625 69.6872 C 232.621 69.6872 231.812 68.8792 231.812 67.8752 C 231.812 66.8752 232.621 66.0624 233.625 66.0624 C 234.629 66.0624 235.438 66.8752 235.438 67.8752 Z" fill="black"/>
<path d="M 235.438 67.8752 C 235.438 68.8792 234.629 69.6872 233.625 69.6872 C 232.621 69.6872 231.812 68.8792 231.812 67.8752 C 231.812 66.8752 232.621 66.0624 233.625 66.0624 C 234.629 66.0624 235.438 66.8752 235.438 67.8752 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 307.625 126 L 307.625 191 L 308 240.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 185.312 180.375 L 125.312 180.375 L 125.312 127.875 L 132.812 135.375 L 125.312 127.875 L 117.812 135.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 188.938 180.3752 C 188.938 181.3792 188.129 182.1872 187.125 182.1872 C 186.121 182.1872 185.312 181.3792 185.312 180.3752 C 185.312 179.3752 186.121 178.5624 187.125 178.5624 C 188.129 178.5624 188.938 179.3752 188.938 180.3752 Z" fill="black"/>
<path d="M 188.938 180.3752 C 188.938 181.3792 188.129 182.1872 187.125 182.1872 C 186.121 182.1872 185.312 181.3792 185.312 180.3752 C 185.312 179.3752 186.121 178.5624 187.125 178.5624 C 188.129 178.5624 188.938 179.3752 188.938 180.3752 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<text transform="translate(247.875 16.375002)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">vdd</tspan>
</text>
<text transform="translate(97.875 108.0882)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">DA</tspan>
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="15.785156" y="11">T</tspan>
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="22.23047" y="11">A</tspan>
</text>
<text transform="translate(421.125 320.125)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">br</tspan>
</text>
<text transform="translate(22.125 322.375)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">bl</tspan>
</text>
<text transform="translate(176.625 328.375)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">en</tspan>
</text>
<text transform="translate(358.875 173.875)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">en</tspan>
</text>
<text transform="translate(88.784 169.1241)" fill="black">
<tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="0" y="11">en</tspan>
</text>
<path d="M 94.07 213.621 C 96.141 213.621 97.82 215.297 97.82 217.371 C 97.82 219.438 96.141 221.121 94.07 221.121 C 91.996 221.121 90.32 219.438 90.32 217.371 C 90.32 215.297 91.996 213.621 94.07 213.621 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 52.875 239.906 L 80.571 239.906 L 80.571 226.164 L 115.184 226.164 L 115.184 239.906 L 142.875 239.906" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="80.7325" y1="221.367" x2="115.0175" y2="221.367" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 364.07 214.41 C 366.141 214.41 367.82 216.086 367.82 218.16 C 367.82 220.23 366.141 221.91 364.07 221.91 C 361.996 221.91 360.32 220.23 360.32 218.16 C 360.32 216.086 361.996 214.41 364.07 214.41 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 320.566 239.91 L 348.262 239.91 L 348.262 226.164 L 382.875 226.164 L 382.875 239.91 L 410.566 239.91" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="348.59" y1="222.375" x2="382.875" y2="222.375" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<line x1="346.184" y1="226.164" x2="384.258" y2="226.164" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 160.391 125.535 L 160.391 190.535 L 160.766 239.91" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 161.391 165.5978 C 161.391 166.6018 160.582 167.4098 159.578 167.4098 C 158.574 167.4098 157.765 166.6018 157.765 165.5978 C 157.765 164.5978 158.574 163.785 159.578 163.785 C 160.582 163.785 161.391 164.5978 161.391 165.5978 Z" fill="black"/>
<path d="M 161.391 165.5978 C 161.391 166.6018 160.582 167.4098 159.578 167.4098 C 158.574 167.4098 157.765 166.6018 157.765 165.5978 C 157.765 164.5978 158.574 163.785 159.578 163.785 C 160.582 163.785 161.391 164.5978 161.391 165.5978 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 163.391 165.9136 L 165.391 165.9136 L 218.9765 165.42652 L 272.562 164.93945 L 274.562 164.93945" marker-end="url(#FilledCenterBall_Marker)" marker-start="url(#FilledCenterBall_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
<path d="M 190.8749 202.82488 L 246.992 202.3623 L 303.1091 201.89973" marker-end="url(#FilledCenterBall_Marker)" marker-start="url(#FilledCenterBall_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
<path d="M 162.704 239.6256 C 162.704 240.6296 161.895 241.4376 160.891 241.4376 C 159.887 241.4376 159.078 240.6296 159.078 239.6256 C 159.078 238.6256 159.887 237.8128 160.891 237.8128 C 161.895 237.8128 162.704 238.6256 162.704 239.6256 Z" fill="black"/>
<path d="M 162.704 239.6256 C 162.704 240.6296 161.895 241.4376 160.891 241.4376 C 159.887 241.4376 159.078 240.6296 159.078 239.6256 C 159.078 238.6256 159.887 237.8128 160.891 237.8128 C 161.895 237.8128 162.704 238.6256 162.704 239.6256 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
<path d="M 309.688 240.0294 C 309.688 241.0334 308.879 241.8414 307.875 241.8414 C 306.871 241.8414 306.062 241.0334 306.062 240.0294 C 306.062 239.0294 306.871 238.2166 307.875 238.2166 C 308.879 238.2166 309.688 239.0294 309.688 240.0294 Z" fill="black"/>
<path d="M 309.688 240.0294 C 309.688 241.0334 308.879 241.8414 307.875 241.8414 C 306.871 241.8414 306.062 241.0334 306.062 240.0294 C 306.062 239.0294 306.871 238.2166 307.875 238.2166 C 308.879 238.2166 309.688 239.0294 309.688 240.0294 Z" stroke="black" stroke-linecap="round" stroke-linejoin="miter" stroke-width="1"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="76" 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="76" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h49v20H0z"/><path fill="#97CA00" d="M49 0h27v20H49z"/><path fill="url(#b)" d="M0 0h76v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="255" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="390">Python</text><text x="255" y="140" transform="scale(.1)" textLength="390">Python</text><text x="615" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="170">3.5</text><text x="615" y="140" transform="scale(.1)" textLength="170">3.5</text></g> </svg>

After

Width:  |  Height:  |  Size: 948 B

BIN
images/SCMOS_16kb_sram.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="106" 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="106" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#007ec6" d="M63 0h43v20H63z"/><path fill="url(#b)" d="M0 0h106v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">download</text><text x="325" y="140" transform="scale(.1)" textLength="530">download</text><text x="835" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="330">stable</text><text x="835" y="140" transform="scale(.1)" textLength="330">stable</text></g> </svg>

After

Width:  |  Height:  |  Size: 961 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120" 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="120" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#a)"><path fill="#555" d="M0 0h63v20H0z"/><path fill="#007ec6" d="M63 0h57v20H63z"/><path fill="url(#b)" d="M0 0h120v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110"> <text x="325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="530">download</text><text x="325" y="140" transform="scale(.1)" textLength="530">download</text><text x="905" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="470">unstable</text><text x="905" y="140" transform="scale(.1)" textLength="470">unstable</text></g> </svg>

After

Width:  |  Height:  |  Size: 965 B

View File

@ -1,2 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 1004 B