Merge branch 'dev' into datasheet_gen

This commit is contained in:
Jesse Cirimelli-Low 2018-11-20 11:23:42 -08:00
commit 1942ef33ac
31 changed files with 736 additions and 828 deletions

View File

@ -1,6 +1,7 @@
# 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)
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)
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)
[![Download](images/download.svg)](https://github.com/VLSIDA/PrivateRAM/archive/master.zip)
[![License: BSD 3-clause](./images/license_badge.svg)](./LICENSE)
An open-source static random access memory (SRAM) compiler.
@ -38,6 +39,12 @@ For example add this to your .bashrc:
export OPENRAM_TECH="$HOME/openram/technology"
```
You may also wish to add OPENRAM\_HOME to your PYTHONPATH:
```
export PYTHONPATH="$PYTHONPATH:$OPENRAM_HOME"
```
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
@ -55,8 +62,7 @@ We have included the most recent SCN4M_SUBM design rules from [Qflow].
# Basic Usage
Once you have defined the environment, you can run OpenRAM from the command line
using a single configuration file written in Python. You may wish to add
$OPENRAM\_HOME to your $PYTHONPATH.
using a single configuration file written in Python.
For example, create a file called *myconfig.py* specifying the following
parameters for your memory:
@ -67,7 +73,7 @@ word_size = 2
# Number of words in the memory
num_words = 16
# Technology to use in $OPENRAM\_TECH
# Technology to use in $OPENRAM_TECH
tech_name = "scn4m_subm"
# Process corners to characterize
process_corners = ["TT"]
@ -87,7 +93,7 @@ output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
You can then run OpenRAM by executing:
```
python3 $OPENRAM\_HOME/openram.py myconfig
python3 $OPENRAM_HOME/openram.py myconfig
```
You can see all of the options for the configuration file in
$OPENRAM\_HOME/options.py
@ -175,7 +181,7 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
- [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
- [James Stine] from [VLSIARCH] co-founded the project.
- Hunter Nichols maintains and updates the timing characterization.
- Michael Grims created and maintains the multiport netlist code.
- 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.
@ -184,6 +190,8 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
- 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
@ -192,9 +200,9 @@ OpenRAM is licensed under the [BSD 3-clause License](./LICENSE).
[VLSIARCH]: https://vlsiarch.ecen.okstate.edu/
[OpenRAMpaper]: https://ieeexplore.ieee.org/document/7827670/
[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
[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

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

@ -35,20 +35,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