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:
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:
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
accepted and help other OpenRAM users.
# Directory Structure
* compiler - openram compiler itself (pointed to by OPENRAM_HOME)
* compiler/base - base data structure modules
* compiler/pgates - parameterized cells (e.g. logic gates)
* compiler/bitcells - various bitcell styles
* compiler/modules - high-level modules (e.g. decoders, etc.)
* compiler/verify - DRC and LVS verification wrappers
* compiler/characterizer - timing characterization code
* compiler/gdsMill - GDSII reader/writer
* compiler/router - router for signals and power supplies
* compiler/tests - unit tests
* technology - openram technology directory (pointed to by OPENRAM_TECH)
* technology/freepdk45 - example configuration library for [FreePDK45 technology node
* technology/scn4m_subm - example configuration library [SCMOS] technology node
* technology/scn3me_subm - unsupported configuration (not enough metal layers)
* technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies
* docs - LaTeX manual (outdated)
* lib - IP library of pregenerated memories
# Code Style
Our code may not be the best and we acknowledge that. We welcome

210
README.md
View File

@ -1,83 +1,119 @@
# 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)
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.
# 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
The OpenRAM compiler has very few dependencies:
+ [Ngspice] 26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
+ Python 3.5 and higher
+ Python 3.5 or higher
+ Python numpy (pip3 install numpy to install)
+ flask_table (pip3 install flask to install)
If you want to perform DRC and LVS, you will need either:
+ Calibre (for [FreePDK45] or [SCMOS])
+ [Magic] + [Netgen] (for [SCMOS] only)
+ Calibre (for [FreePDK45])
+ [Magic] + [Netgen] (for [SCMOS])
You must set two environment variables:
+ OPENRAM\_HOME should point to the compiler source directory.
+ OPENERAM\_TECH should point to a root technology directory.
For example add this to your .bashrc:
You must set two environment variables: OPENRAM\_HOME should point to
the compiler source directory. OPENERAM\_TECH should point to a root
technology directory that contains subdirs of all other technologies.
For example, in bash, add to your .bashrc:
```
export OPENRAM_HOME="$HOME/openram/compiler"
export OPENRAM_TECH="$HOME/openram/technology"
```
For example, in csh/tcsh, add to your .cshrc/.tcshrc:
You may also wish to add OPENRAM\_HOME to your PYTHONPATH:
```
setenv OPENRAM_HOME "$HOME/openram/compiler"
setenv OPENRAM_TECH "$HOME/openram/technology"
export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME"
```
We include the tech files necessary for [FreePDK45] and [SCMOS]. The [SCMOS]
spice models, however, are generic and should be replaced with foundry
models.
If you are using [FreePDK45], you should also have that set up and have the
environment variable point to the PDK.
For example, in bash, add to your .bashrc:
We include the tech files necessary for [FreePDK45] and [SCMOS]
SCN4M_SUBM. The [SCMOS] spice models, however, are generic and should
be replaced with foundry models. If you are using [FreePDK45], you
should also have that set up and have the environment variable point
to the PDK. For example add this to your .bashrc:
```
export FREEPDK45="/bsoe/software/design-kits/FreePDK45"
```
For example, in csh/tcsh, add to your .tcshrc:
```
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].
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)
* 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
Once you have defined the environment, you can run OpenRAM from the command line
using a single configuration file written in Python.
For example, create a file called *myconfig.py* specifying the following
parameters for your memory:
```
# Data word size
word_size = 2
# Number of words in the memory
num_words = 16
# Technology to use in $OPENRAM_TECH
tech_name = "scn4m_subm"
# Process corners to characterize
process_corners = ["TT"]
# Voltage corners to characterize
supply_voltages = [ 3.3 ]
# Temperature corners to characterize
temperatures = [ 25 ]
# Output directory for the results
output_path = "temp"
# Output file base name
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
# Disable analytical models for full characterization (WARNING: slow!)
# analytical_delay = False
```
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
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
```
@ -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.
# Creating Custom Technologies
# Porting to a New Technology
If you want to support a enw technology, you will need to create:
+ a setup script for each technology you want to use
+ a technology directory for each technology with the base cells
All setup scripts should be in the setup\_scripts directory under the
$OPENRAM\_TECH directory. We provide two technology examples for [SCMOS] and [FreePDK45].
Please look at the following file for an example of what is needed for OpenRAM:
$OPENRAM\_TECH directory. We provide two technology examples for
[SCMOS] and [FreePDK45]. Please look at the following file for an
example of what is needed for OpenRAM:
```
$OPENRAM_TECH/setup_scripts/setup_openram_freepdk45.py
```
Each setup script should be named as: setup\_openram\_{tech name}.py.
Each specific technology (e.g., [FreePDK45]) should be a subdirectory
(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files:
1. gds_lib folder with all the .gds (premade) library cells. At a
minimum this includes:
* ms_flop.gds
* sense_amp.gds
* write_driver.gds
* cell_6t.gds
* replica_cell_6t.gds
* tri_gate.gds
2. sp_lib folder with all the .sp (premade) library netlists for the above cells.
3. layers.map
4. A valid tech Python module (tech directory with __init__.py and tech.py) with:
* References in tech.py to spice models
* DRC/LVS rules needed for dynamic cells and routing
* Layer information
* etc.
* gds_lib folder with all the .gds (premade) library cells:
* dff.gds
* sense_amp.gds
* write_driver.gds
* cell_6t.gds
* replica\_cell\_6t.gds
* sp_lib folder with all the .sp (premade) library netlists for the above cells.
* layers.map
* A valid tech Python module (tech directory with __init__.py and tech.py) with:
* References in tech.py to spice models
* DRC/LVS rules needed for dynamic cells and routing
* Layer information
* Spice and supply information
* etc.
# Get Involved
+ Report bugs by submitting a [Github issue].
+ Report bugs by submitting [Github issues].
+ Develop new features (see [how to contribute](./CONTRIBUTING.md))
+ Submit code/fixes using a [Github pull request]
+ Follow our [project][Github projects].
+ Read and cite our [ICCAD paper][OpenRAMpaper]
# 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
OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
# 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
[Github issues]: https://github.com/PrivateRAM/PrivateRAM/issues
[Github pull request]: https://github.com/PrivateRAM/PrivateRAM/pulls
[Github projects]: https://github.com/PrivateRAM/PrivateRAM/projects
[email me]: mailto:mrg+openram@ucsc.edu
[James Stine]: https://ece.okstate.edu/content/stine-james-e-jr-phd
[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/
[Netgen]: http://opencircuitdesign.com/netgen/
[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
[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"))
m1m2 = contact(layer_stack=("metal1", "via1", "metal2"))
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.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 = self.m2_pitch
self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space)
def setup_drc_constants(self):
""" 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.m3_width = drc("minwidth_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_space = drc("active_to_body_active")
self.contact_width = drc("minwidth_contact")
@ -65,8 +65,12 @@ class design(hierarchy_design):
self.readwrite_ports = []
# These are the read/write and write-only port indices
self.write_ports = []
# These are the write-only port indices.
self.writeonly_ports = []
# These are teh read/write and read-only port indice
self.read_ports = []
# These are the read-only port indices.
self.readonly_ports = []
# These are all the ports
self.all_ports = list(range(total_ports))
@ -78,9 +82,11 @@ class design(hierarchy_design):
port_number += 1
for port in range(OPTS.num_w_ports):
self.write_ports.append(port_number)
self.writeonly_ports.append(port_number)
port_number += 1
for port in range(OPTS.num_r_ports):
self.read_ports.append(port_number)
self.readonly_ports.append(port_number)
port_number += 1
def analytical_power(self, proc, vdd, temp, load):

View File

@ -200,18 +200,19 @@ class instance(geometry):
self.mod.gds_write_file(self.gds)
# now write an instance of my module/structure
new_layout.addInstance(self.gds,
self.mod.name,
offsetInMicrons=self.offset,
mirror=self.mirror,
rotate=self.rotate)
def place(self, offset, mirror="R0", rotate=0):
""" This updates the placement of an instance. """
debug.info(3, "placing instance {}".format(self.name))
# Update the placement of an already added instance
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
self.rotate = rotate
self.update_boundary()
debug.info(3, "placing instance {}".format(self))
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.
# These modules ensure unique names or have no changes if they
# aren't unique
ok_list = ['ms_flop',
'dff',
'dff_buf',
'bitcell',
'contact',
ok_list = ['contact',
'ptx',
'pbitcell',
'replica_pbitcell',
'sram',
'hierarchical_predecode2x4',
'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)
else:
# Name is in our list of exceptions (they don't change)
for ok_names in ok_list:
if ok_names in self.__class__.__name__:
if ok_names == self.__class__.__name__:
break
else:
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):
"""Reads a GDSII file in the library and checks if it exists
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:
self.gds = None
return
@ -454,7 +459,6 @@ class layout(lef.lef):
# open the gds file if it exists or else create a blank layout
if os.path.isfile(self.gds_file):
debug.info(3, "opening {}".format(self.gds_file))
self.is_library_cell=True
self.gds = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(self.gds)
reader.loadFromFile(self.gds_file)

View File

@ -62,6 +62,24 @@ class pin_layout:
else:
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):
"""
Inflate the rectangle by the spacing (or other rule)
@ -325,7 +343,7 @@ class pin_layout:
(r2_ll,r2_ur) = other.rect
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
right = r1_ur.x < r2_ll.x

View File

@ -148,13 +148,13 @@ class VlsiLayout:
structureNames=[]
for name in self.structures:
structureNames.append(name)
for name in self.structures:
if(len(self.structures[name].srefs)>0): #does this structure reference any others?
for sref in self.structures[name].srefs: #go through each reference
if sref.sName in structureNames: #and compare to our list
structureNames.remove(sref.sName)
debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames)))
self.rootStructureName = structureNames[0]
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
@ -304,6 +304,7 @@ class VlsiLayout:
debug.info(1,"DEBUG: Structure %s Found"%StructureName)
StructureFound = True
debug.check(StructureFound,"Could not find layout to instantiate {}".format(StructureName))
# 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()
# 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()
def add_pins(self):
""" Adding pins for Bank module"""
for port in self.read_ports:
@ -232,8 +232,8 @@ class bank(design.design):
self.row_decoder_offsets[port] = vector(-x_offset,0)
# LOWER LEFT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
# Below the bitcell array
# Place the col decoder left aligned with row decoder (x_offset doesn't change)
# Below the bitcell array with well spacing
if self.col_addr_size > 0:
y_offset = self.column_decoder.height
else:
@ -290,8 +290,11 @@ class bank(design.design):
# UPPER RIGHT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
# Below the bitcell array
y_offset = self.bitcell_array.height + self.m2_gap
# Above the bitcell array with a well spacing
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")
self.column_decoder_offsets[port] = vector(x_offset,y_offset)
@ -723,7 +726,7 @@ class bank(design.design):
for port in self.all_ports:
if port%2 == 1:
mirror = "MY"
mirror = "XY"
else:
mirror = "R0"
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:
return
bottom_inst = self.column_mux_array_inst[port]
top_inst = self.precharge_array_inst[port]
self.connect_bitlines(top_inst, bottom_inst, self.num_cols)
inst1 = self.column_mux_array_inst[port]
inst2 = self.precharge_array_inst[port]
self.connect_bitlines(inst1, inst2, self.num_cols)
def route_column_mux_to_bitcell_array(self, port):
""" Routing of BL and BR between col mux bitcell array """
@ -884,47 +887,50 @@ class bank(design.design):
if self.col_addr_size==0:
return
bottom_inst = self.column_mux_array_inst[port]
top_inst = self.bitcell_array_inst
self.connect_bitlines(top_inst, bottom_inst, self.num_cols)
inst2 = self.column_mux_array_inst[port]
inst1 = self.bitcell_array_inst
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):
""" 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:
# Sense amp is connected to the col mux
top_inst = self.column_mux_array_inst[port]
top_bl = "bl_out_{}"
top_br = "br_out_{}"
inst1 = self.column_mux_array_inst[port]
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
else:
# Sense amp is directly connected to the precharge array
top_inst = self.precharge_array_inst[port]
top_bl = "bl_{}"
top_br = "br_{}"
inst1 = self.precharge_array_inst[port]
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size,
inst1_bl_name=top_bl, inst1_br_name=top_br)
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
bottom_inst = self.write_driver_array_inst[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 bitcell array """
inst2 = self.write_driver_array_inst[port]
if self.col_addr_size>0:
# Sense amp is connected to the col mux
top_inst = self.column_mux_array_inst[port]
top_bl = "bl_out_{}"
top_br = "br_out_{}"
# Write driver is connected to the col mux
inst1 = self.column_mux_array_inst[port]
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
else:
# Sense amp is directly connected to the precharge array
top_inst = self.precharge_array_inst[port]
top_bl = "bl_{}"
top_br = "br_{}"
# Write driver is directly connected to the bitcell array
inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}"
self.connect_bitlines(inst1=top_inst, inst2=bottom_inst, num_bits=self.word_size,
inst1_bl_name=top_bl, inst1_br_name=top_br)
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
def route_write_driver_to_sense_amp(self, port):
""" 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):
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,
offset=data_pin.center(),
height=data_pin.height(),
@ -965,6 +971,37 @@ class bank(design.design):
din_name = "din{0}_{1}".format(port,row)
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,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
@ -1189,8 +1226,8 @@ class bank(design.design):
# clk to wordline_driver
control_signal = self.prefix+"clk_buf{}".format(port)
pin_pos = self.wordline_driver_inst[port].get_pin("en").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
pin_pos = self.wordline_driver_inst[port].get_pin("en").bc()
mid_pos = pin_pos - vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[port][control_signal].x
control_pos = vector(control_x_offset, mid_pos.y)
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 globals import OPTS
unique_id = 1
class bitcell_array(design.design):
"""
@ -12,8 +12,13 @@ class bitcell_array(design.design):
and word line is connected by abutment.
Connects the word lines and bit lines.
"""
unique_id = 1
def __init__(self, cols, rows, name=""):
def __init__(self, cols, rows, name="bitcell_array"):
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)
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):
""" Create layout and route between modules """
self.route_rails()
self.place_instances()
self.route_all()
@ -149,7 +148,7 @@ class control_logic(design.design):
def route_rails(self):
""" 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)
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
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_we_row(row=row)
pre_height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.by()
height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.uy()
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_in_row(row=row)
self.place_sen_row(row=row+1)
self.place_rbl(row=row+2)
pre_height = self.rbl_inst.uy()
height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by()
# 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)
# 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
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
@ -205,6 +204,7 @@ class control_logic(design.design):
def route_all(self):
""" Routing between modules """
self.route_rails()
self.route_dffs()
if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen()

View File

@ -12,11 +12,13 @@ class dff_buf(design.design):
with two inverters, of variable size, to provide q
and qbar. This is to enable driving large fanout loads.
"""
unique_id = 1
def __init__(self, inv1_size=2, inv2_size=4, 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)
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.
Unlike the data flops, these are never spaced out.
"""
unique_id = 1
def __init__(self, rows, columns, inv1_size=2, inv2_size=4, name=""):
self.rows = rows
self.columns = columns
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)
debug.info(1, "Creating {}".format(self.name))
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
do not have Qbar, so this will create it.
"""
unique_id = 1
def __init__(self, inv_size=2, 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)
debug.info(1, "Creating {}".format(self.name))
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.
Unlike the data flops, these are never spaced out.
"""
unique_id = 1
def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows
self.columns = columns
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)
debug.info(1, "Creating {}".format(self.name))
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
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):
# 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)
# 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)
# 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 math
from direction import direction
from vector3d import vector3d
@ -139,3 +140,16 @@ def flatten_set(curset):
else:
newset.update(flatten_set(c))
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

@ -464,16 +464,23 @@ class pin_group:
# If it is contained, it won't need a connector
if pin.contained_by_any(self.enclosures):
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)
right_connector = self.find_right_connector(pin, self.enclosures)
above_connector = self.find_above_connector(pin, self.enclosures)
below_connector = self.find_below_connector(pin, self.enclosures)
for connector in [left_connector, right_connector, above_connector, below_connector]:
if connector:
self.enclosures.append(connector)
connector_list = [left_connector, right_connector, above_connector, below_connector]
filtered_list = list(filter(lambda x: x!=None, connector_list))
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
for pin_list in self.pins:
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.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):
"""
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
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 in pin_list:
@ -593,7 +610,7 @@ class pin_group:
# Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
pin_set.update(pin_in_tracks)
if len(pin_set)==0:
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
self.router.write_debug_gds("blocked_pin.gds")
@ -639,7 +656,6 @@ class pin_group:
that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure.
"""
additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set))
@ -663,3 +679,6 @@ class pin_group:
self.set_routed()
self.enclosures = self.compute_enclosures()

View File

@ -9,9 +9,10 @@ from pin_layout import pin_layout
from pin_group import pin_group
from vector import vector
from vector3d import vector3d
from globals import OPTS
from globals import OPTS,print_time
from pprint import pformat
import grid_utils
from datetime import datetime
class router(router_tech):
"""
@ -31,16 +32,18 @@ class router(router_tech):
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
#start_time = datetime.now()
if not gds_filename:
gds_filename = OPTS.openram_temp+"temp.gds"
self.cell.gds_write(gds_filename)
# Load the gds file and read in all the shapes
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gds_filename)
self.top_name = self.layout.rootStructureName
#print_time("GDS read",datetime.now(), start_time)
### The pin data structures
# A map of pin names to a set of pin_layout structures
self.pins = {}
@ -127,8 +130,12 @@ class router(router_tech):
Pin can either be a label or a location,layer pair: [[x,y],layer].
"""
debug.info(1,"Finding pins for {}.".format(pin_name))
#start_time = datetime.now()
self.retrieve_pins(pin_name)
#print_time("Retrieved pins",datetime.now(), start_time)
#start_time = datetime.now()
self.analyze_pins(pin_name)
#print_time("Analyzed pins",datetime.now(), start_time)
def find_blockages(self):
"""
@ -152,97 +159,98 @@ class router(router_tech):
self.find_pins(pin)
# 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()
#print_time("Find blockags",datetime.now(), start_time)
# Convert the blockages to grid units
#start_time = datetime.now()
self.convert_blockages()
#print_time("Find blockags",datetime.now(), start_time)
# This will convert the pins to grid units
# 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:
self.convert_pins(pin)
#print_time("Convert pins",datetime.now(), start_time)
#start_time = datetime.now()
for pin in pin_list:
self.combine_adjacent_pins(pin)
#print_time("Combine pins",datetime.now(), start_time)
#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
# Must be done before enclosing pins
#start_time = datetime.now()
self.separate_adjacent_pins(self.supply_rail_space_width)
#print_time("Separate pins",datetime.now(), start_time)
# For debug
#self.separate_adjacent_pins(1)
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
#start_time = datetime.now()
self.enclose_pins()
#print_time("Enclose pins",datetime.now(), start_time)
#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
single pin_group. The pins themselves may not be touching, but
enclose_pis in the next step will ensure they are touching.
"""
# Make a copy since we are going to add to (and then reduce) this list
pin_groups = self.pin_groups[pin_name].copy()
# Start as None to signal the first iteration
remove_indices = set()
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# Find all adjacencies
adjacent_pins = {}
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]):
# Cannot combine with yourself
if index1==index2:
# Cannot combine with yourself, also don't repeat
if index1<=index2:
continue
# Cannot combine more than once
if index2 in remove_indices:
continue
# Combine if at least 1 grid cell is adjacent
if pg1.adjacent(pg2):
combined = pin_group(pin_name, [], self)
combined.combine_groups(pg1, pg2)
debug.info(3,"Combining {0} {1} {2}:".format(pin_name, index1, index2))
debug.info(3, " {0}\n {1}".format(pg1.pins, pg2.pins))
debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids))
remove_indices.update([index1,index2])
pin_groups.append(combined)
break
if not index1 in adjacent_pins.keys():
adjacent_pins[index1] = set([index2])
else:
adjacent_pins[index1].add(index2)
# Remove them in decreasing order to not invalidate the indices
debug.info(4,"Removing {}".format(sorted(remove_indices)))
for i in sorted(remove_indices, reverse=True):
del pin_groups[i]
# Use the new pin group!
self.pin_groups[pin_name] = pin_groups
# 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]))])
removed_pairs = int(len(remove_indices)/2)
debug.info(1, "Combined {0} pin pairs for {1}".format(removed_pairs,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))
new_pin_groups.append(combined)
# Add the pin groups that weren't added to the new set
for index in all_indices:
new_pin_groups.append(self.pin_groups[pin_name][index])
old_size = len(self.pin_groups[pin_name])
# Use the new pin group!
self.pin_groups[pin_name] = new_pin_groups
removed_pairs = old_size - len(new_pin_groups)
debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name))
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):
"""
@ -271,7 +279,7 @@ class router(router_tech):
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
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)
# These should have the same length, so...
if len(grids_g1)>0:
@ -507,7 +515,7 @@ class router(router_tech):
# scale the size bigger to include neaby tracks
ll=ll.scale(self.track_factor).floor()
ur=ur.scale(self.track_factor).ceil()
#print(pin)
# Keep tabs on tracks with sufficient and insufficient overlap
sufficient_list = set()
insufficient_list = set()
@ -515,35 +523,50 @@ class router(router_tech):
zindex=self.get_zindex(pin.layer_num)
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):
debug.info(4,"Converting [ {0} , {1} ]".format(x,y))
(full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
if full_overlap:
sufficient_list.update([full_overlap])
if 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:
return sufficient_list
elif expansion==0 and len(insufficient_list)>0:
#Remove blockages and return the best to be patched
insufficient_list.difference_update(self.blocked_grids)
return self.get_best_offgrid_pin(pin, insufficient_list)
best_pin = self.get_all_offgrid_pin(pin, insufficient_list)
#print(best_pin)
return best_pin
elif expansion>0:
#Remove blockages and return the nearest
insufficient_list.difference_update(self.blocked_grids)
return self.get_nearest_offgrid_pin(pin, insufficient_list)
nearest_pin = self.get_furthest_offgrid_pin(pin, insufficient_list)
return nearest_pin
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):
"""
Given a pin and a list of partial overlap grids:
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.
Find a list of the single pin with the most overlap.
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
@ -560,6 +583,23 @@ class router(router_tech):
best_coord=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):
"""

View File

@ -2,13 +2,14 @@ import gdsMill
import tech
import math
import debug
from globals import OPTS
from globals import OPTS,print_time
from contact import contact
from pin_group import pin_group
from pin_layout import pin_layout
from vector3d import vector3d
from router import router
from direction import direction
from datetime import datetime
import grid
import grid_utils
@ -68,10 +69,13 @@ class supply_router(router):
self.compute_supply_rail_dimensions()
# Get the pin shapes
#start_time = datetime.now()
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)
# Add the supply rails in a mesh network and connect H/V with vias
#start_time = datetime.now()
# Block everything
self.prepare_blockages(self.gnd_name)
# Determine the rail locations
@ -82,15 +86,20 @@ class supply_router(router):
# Determine the rail locations
self.route_supply_rails(self.vdd_name,1)
#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(gnd_name)
#print_time("Simple overlaps",datetime.now(), start_time)
#self.write_debug_gds("debug_simple_route.gds",stop_program=False)
# Route the supply pins to the supply rails
# 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(gnd_name)
#print_time("Routing",datetime.now(), start_time)
#self.write_debug_gds("debug_pin_routes.gds",stop_program=True)
#self.write_debug_gds("final.gds",False)

View File

@ -164,8 +164,12 @@ class vector3d():
return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z))
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)
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):

View File

@ -43,10 +43,10 @@ class sram_1bank(sram_base):
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
logic.
This places the instances for a single bank SRAM with control
logic and up to 2 ports.
"""
# No orientation or offset
@ -57,39 +57,77 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array)
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs.
for port in self.all_ports:
control_pos = vector(-self.control_logic_rw.width - 2*self.m2_pitch,
self.bank.bank_center.y - self.control_logic_rw.control_logic_center.y)
self.control_logic_insts[port].place(control_pos)
control_pos = [None]*len(self.all_ports)
row_addr_pos = [None]*len(self.all_ports)
col_addr_pos = [None]*len(self.all_ports)
data_pos = [None]*len(self.all_ports)
# 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)
# 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)
# Port 0
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:
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_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port])
# 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)
# Add the column address below the bank under the control
# The column address flops are aligned with the data flops
# Add the data flops below the bank to the right of the lower-left of bank array
# This relies on the lower-left 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_ll.x,
-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 = vector(self.bank.bank_center.x - self.col_addr_dff.width - self.bank.central_bus_width,
data_gap - self.col_addr_dff.height)
self.col_addr_dff_insts[port].place(col_addr_pos)
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 below the bank to the right of the center of bank:
# This relies on the center point of the bank:
# 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.
data_pos = vector(self.bank.bank_center.x,
data_gap - self.data_dff.height)
self.data_dff_insts[port].place(data_pos)
# two supply rails are already included in the bank, so just 2 here.
# self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
# self.height = self.bank.height
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")
def add_layout_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
@ -114,7 +152,7 @@ class sram_1bank(sram_base):
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))
def route(self):
def route_layout(self):
""" Route a single bank SRAM """
self.add_layout_pins()
@ -151,20 +189,27 @@ class sram_1bank(sram_base):
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
# This uses a metal2 track to the right of the control/row addr DFF
# to route vertically.
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
# 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_pos = control_clk_buf_pin.rc()
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
row_addr_clk_pos = row_addr_clk_pin.rc()
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
row_addr_clk_pos.y)
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()
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
row_addr_clk_pos.y)
mid2_pos = vector(mid1_pos.x,
control_clk_buf_pos.y)
# Note, the via to the control logic is taken care of when we route

View File

@ -75,8 +75,9 @@ class sram_base(design):
def create_layout(self):
""" Layout creation """
self.place_modules()
self.route()
self.place_instances()
self.route_layout()
self.add_lvs_correspondence_points()
@ -225,12 +226,12 @@ class sram_base(design):
words_per_row=self.words_per_row,
port_type="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,
words_per_row=self.words_per_row,
port_type="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,
words_per_row=self.words_per_row,
port_type="r")
@ -369,9 +370,13 @@ class sram_base(design):
def create_data_dff(self):
""" Add and place all data flops """
insts = []
for port in self.write_ports:
insts.append(self.add_inst(name="data_dff{}".format(port),
mod=self.data_dff))
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="data_dff{}".format(port),
mod=self.data_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
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
"""
Run a test on a delay chain
Run a test on a replica bitline
"""
import unittest
@ -32,61 +32,6 @@ class replica_bitline_test(openram_test):
a = replica_bitline.replica_bitline(stages,fanout,rows)
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()

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"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
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>
<!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/">
<metadata> Produced by OmniGraffle 7.6.1
<dc:date>2018-11-16 00:52:28 +0000</dc:date>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g3496-2"
transform="matrix(0,-1,1,0,-62.34811,791.36093)">
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
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" />
<defs>
<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">
<font-face-src>
<font-face-name name="Helvetica"/>
</font-face-src>
</font-face>
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledCenterBall_Marker" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="black">
<g>
<circle cx="2.9999986" cy="0" r="2.999997" fill="currentColor" stroke="currentColor" stroke-width="1"/>
</g>
<g
transform="matrix(1.25,0,0,1.25,240,223.19311)"
id="g3015-0">
<path
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" />
</marker>
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledCenterBall_Marker_2" viewBox="-7 -4 8 8" markerWidth="8" markerHeight="8" color="black">
<g>
<circle cx="-2.9999986" cy="0" r="2.999997" fill="currentColor" stroke="currentColor" stroke-width="1"/>
</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>
</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