Merge branch 'dev' into bisr

This commit is contained in:
Aditi Sinha 2020-02-20 17:09:09 +00:00
commit 34939ebd70
87 changed files with 3360 additions and 2302 deletions

View File

@ -7,8 +7,11 @@
#
import hierarchy_design
import debug
from tech import drc, layer
from tech import drc
import tech
from vector import vector
from sram_factory import factory
import sys
class contact(hierarchy_design.hierarchy_design):
@ -26,29 +29,36 @@ class contact(hierarchy_design.hierarchy_design):
"""
def __init__(self, layer_stack, dimensions=(1, 1), directions=("V", "V"),
def __init__(self, layer_stack, dimensions=(1, 1), directions=None,
implant_type=None, well_type=None, name=""):
# This will ignore the name parameter since
# we can guarantee a unique name here
hierarchy_design.hierarchy_design.__init__(self, name)
debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack))
self.add_comment("dimensions: {0}".format(dimensions))
if implant_type or well_type:
self.add_comment("implant type: {}\n".format(implant_type))
self.add_comment("well_type: {}\n".format(well_type))
self.is_well_contact = implant_type == well_type
self.layer_stack = layer_stack
self.dimensions = dimensions
self.directions = directions
if directions:
self.directions = directions
else:
self.directions = (tech.preferred_directions[layer_stack[0]],
tech.preferred_directions[layer_stack[2]])
self.offset = vector(0, 0)
self.implant_type = implant_type
self.well_type = well_type
# Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout()
def create_layout(self):
self.setup_layers()
@ -56,15 +66,19 @@ class contact(hierarchy_design.hierarchy_design):
self.create_contact_array()
self.create_first_layer_enclosure()
self.create_second_layer_enclosure()
self.create_nitride_cut_enclosure()
self.height = max(obj.offset.y + obj.height for obj in self.objs)
self.width = max(obj.offset.x + obj.width for obj in self.objs)
self.height = max(self.first_layer_position.y + self.first_layer_height,
self.second_layer_position.y + self.second_layer_height)
self.width = max(self.first_layer_position.x + self.first_layer_width,
self.second_layer_position.x + self.second_layer_width)
# Do not include the select layer in the height/width
if self.implant_type and self.well_type:
self.create_implant_well_enclosures()
elif self.implant_type or self.well_type:
debug.error(-1, "Must define both implant and well type or none at all.")
debug.error(-1,
"Must define both implant and well type or none.")
def setup_layers(self):
""" Locally assign the layer names. """
@ -72,14 +86,19 @@ class contact(hierarchy_design.hierarchy_design):
(first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer
self.second_layer_name = second_layer
# Contacts will have unique per first layer
if via_layer == "contact":
if via_layer in tech.layer:
self.via_layer_name = via_layer
elif via_layer == "contact":
if first_layer in ("active", "poly"):
self.via_layer_name = first_layer + "_" + via_layer
elif second_layer in ("active", "poly"):
self.via_layer_name = second_layer + "_" + via_layer
else:
self.via_layer_name = second_layer + "_" + via_layer
debug.error("Invalid via layer {}".format(via_layer), -1)
else:
self.via_layer_name = via_layer
debug.error("Invalid via layer {}".format(via_layer), -1)
def setup_layout_constants(self):
""" Determine the design rules for the enclosure layers """
@ -95,42 +114,48 @@ class contact(hierarchy_design.hierarchy_design):
# The extend rule applies to asymmetric enclosures in one direction.
# The enclosure rule applies to symmetric enclosure component.
first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
first_layer_enclosure = drc("{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name))
first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name))
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
# If there's a different rule for active
# FIXME: Make this more elegant
if self.is_well_contact and self.first_layer_name == "active" and "tap_extend_contact" in drc.keys():
self.first_layer_extend = drc("tap_extend_contact")
else:
self.first_layer_extend = drc("{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name))
second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name))
second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name))
self.second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
# In some technologies, the minimum width may be larger
# than the overlap requirement around the via, so
# check this for each dimension.
if self.directions[0] == "V":
self.first_layer_horizontal_enclosure = max(first_layer_enclosure,
(first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(first_layer_extend,
(first_layer_minwidth - self.contact_array_height) / 2)
self.first_layer_horizontal_enclosure = max(self.first_layer_enclosure,
(self.first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(self.first_layer_extend,
(self.first_layer_minwidth - self.contact_array_height) / 2)
elif self.directions[0] == "H":
self.first_layer_horizontal_enclosure = max(first_layer_extend,
(first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(first_layer_enclosure,
(first_layer_minwidth - self.contact_array_height) / 2)
self.first_layer_horizontal_enclosure = max(self.first_layer_extend,
(self.first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(self.first_layer_enclosure,
(self.first_layer_minwidth - self.contact_array_height) / 2)
else:
debug.error("Invalid first layer direction.", -1)
# In some technologies, the minimum width may be larger than the overlap requirement around the via, so
# In some technologies, the minimum width may be larger
# than the overlap requirement around the via, so
# check this for each dimension.
if self.directions[1] == "V":
self.second_layer_horizontal_enclosure = max(second_layer_enclosure,
(second_layer_minwidth - self.contact_array_width) / 2)
self.second_layer_vertical_enclosure = max(second_layer_extend,
(second_layer_minwidth - self.contact_array_height) / 2)
self.second_layer_horizontal_enclosure = max(self.second_layer_enclosure,
(self.second_layer_minwidth - self.contact_array_width) / 2)
self.second_layer_vertical_enclosure = max(self.second_layer_extend,
(self.second_layer_minwidth - self.contact_array_height) / 2)
elif self.directions[1] == "H":
self.second_layer_horizontal_enclosure = max(second_layer_extend,
(second_layer_minwidth - self.contact_array_height) / 2)
self.second_layer_vertical_enclosure = max(second_layer_enclosure,
(second_layer_minwidth - self.contact_array_width) / 2)
self.second_layer_horizontal_enclosure = max(self.second_layer_extend,
(self.second_layer_minwidth - self.contact_array_height) / 2)
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
(self.second_layer_minwidth - self.contact_array_width) / 2)
else:
debug.error("Invalid second layer direction.", -1)
@ -138,11 +163,14 @@ class contact(hierarchy_design.hierarchy_design):
""" Create the contact array at the origin"""
# offset for the via array
self.via_layer_position = vector(
max(self.first_layer_horizontal_enclosure, self.second_layer_horizontal_enclosure),
max(self.first_layer_vertical_enclosure, self.second_layer_vertical_enclosure))
max(self.first_layer_horizontal_enclosure,
self.second_layer_horizontal_enclosure),
max(self.first_layer_vertical_enclosure,
self.second_layer_vertical_enclosure))
for i in range(self.dimensions[1]):
offset = self.via_layer_position + vector(0, self.contact_pitch * i)
offset = self.via_layer_position + vector(0,
self.contact_pitch * i)
for j in range(self.dimensions[0]):
self.add_rect(layer=self.via_layer_name,
offset=offset,
@ -150,14 +178,34 @@ class contact(hierarchy_design.hierarchy_design):
height=self.contact_width)
offset = offset + vector(self.contact_pitch, 0)
def create_nitride_cut_enclosure(self):
""" Special layer that encloses poly contacts in some processes """
# Check if there is a special poly nitride cut layer
if "npc" not in tech.layer:
return
# Only add for poly layers
if self.first_layer_name == "poly":
self.add_rect(layer="npc",
offset=self.first_layer_position,
width=self.first_layer_width,
height=self.first_layer_height)
elif self.second_layer_name == "poly":
self.add_rect(layer="npc",
offset=self.second_layer_position,
width=self.second_layer_width,
height=self.second_layer_height)
def create_first_layer_enclosure(self):
# this is if the first and second layers are different
self.first_layer_position = vector(
max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure, 0),
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure, 0))
self.first_layer_width = self.contact_array_width + 2 * self.first_layer_horizontal_enclosure
self.first_layer_height = self.contact_array_height + 2 * self.first_layer_vertical_enclosure
self.first_layer_width = max(self.contact_array_width + 2 * self.first_layer_horizontal_enclosure,
self.first_layer_minwidth)
self.first_layer_height = max(self.contact_array_height + 2 * self.first_layer_vertical_enclosure,
self.first_layer_minwidth)
self.add_rect(layer=self.first_layer_name,
offset=self.first_layer_position,
width=self.first_layer_width,
@ -169,57 +217,72 @@ class contact(hierarchy_design.hierarchy_design):
max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure, 0),
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure, 0))
self.second_layer_width = self.contact_array_width + 2 * self.second_layer_horizontal_enclosure
self.second_layer_height = self.contact_array_height + 2 * self.second_layer_vertical_enclosure
self.second_layer_width = max(self.contact_array_width + 2 * self.second_layer_horizontal_enclosure,
self.second_layer_minwidth)
self.second_layer_height = max(self.contact_array_height + 2 * self.second_layer_vertical_enclosure,
self.second_layer_minwidth)
self.add_rect(layer=self.second_layer_name,
offset=self.second_layer_position,
width=self.second_layer_width,
height=self.second_layer_height)
def create_implant_well_enclosures(self):
implant_position = self.first_layer_position - [drc("implant_enclosure_active")] * 2
implant_width = self.first_layer_width + 2 * drc("implant_enclosure_active")
implant_height = self.first_layer_height + 2 * drc("implant_enclosure_active")
implant_position = self.first_layer_position - [drc("implant_enclose_active")] * 2
implant_width = self.first_layer_width + 2 * drc("implant_enclose_active")
implant_height = self.first_layer_height + 2 * drc("implant_enclose_active")
self.add_rect(layer="{}implant".format(self.implant_type),
offset=implant_position,
width=implant_width,
height=implant_height)
well_position = self.first_layer_position - [drc("well_enclosure_active")] * 2
well_width = self.first_layer_width + 2 * drc("well_enclosure_active")
well_height = self.first_layer_height + 2 * drc("well_enclosure_active")
self.add_rect(layer="{}well".format(self.well_type),
offset=well_position,
width=well_width,
height=well_height)
# Optionally implant well if layer exists
well_layer = "{}well".format(self.well_type)
if well_layer in tech.layer:
well_width_rule = drc("minwidth_" + well_layer)
self.well_enclose_active = drc(well_layer + "_enclose_active")
self.well_width = max(self.first_layer_width + 2 * self.well_enclose_active,
well_width_rule)
self.well_height = max(self.first_layer_height + 2 * self.well_enclose_active,
well_width_rule)
center_pos = vector(0.5*self.width, 0.5*self.height)
well_position = center_pos - vector(0.5*self.well_width, 0.5*self.well_height)
self.add_rect(layer=well_layer,
offset=well_position,
width=self.well_width,
height=self.well_height)
def analytical_power(self, corner, load):
""" Get total power of a module """
return self.return_power()
from sram_factory import factory
# Set up a static for each layer to be used for measurements
for layer_stack in tech.layer_stacks:
(layer1, via, layer2) = layer_stack
cont = factory.create(module_type="contact",
layer_stack=layer_stack)
module = sys.modules[__name__]
# Also create a contact that is just the first layer
if layer1 == "poly" or layer1 == "active":
setattr(module, layer1 + "_contact", cont)
else:
setattr(module, layer1 + "_via", cont)
# Set up a static for each well contact for measurements
if "nwell" in tech.layer:
cont = factory.create(module_type="contact",
layer_stack=tech.active_stack,
implant_type="n",
well_type="n")
module = sys.modules[__name__]
setattr(module, "nwell_contact", cont)
if "pwell" in tech.layer:
cont = factory.create(module_type="contact",
layer_stack=tech.active_stack,
implant_type="p",
well_type="p")
module = sys.modules[__name__]
setattr(module, "pwell_contact", cont)
# This is not instantiated and used for calculations only.
# These are static 1x1 contacts to reuse in all the design modules.
well = factory.create(module_type="contact",
layer_stack=("active", "contact", "metal1"),
directions=("H", "V"))
active = factory.create(module_type="contact",
layer_stack=("active", "contact", "metal1"),
directions=("H", "V"))
poly = factory.create(module_type="contact",
layer_stack=("poly", "contact", "metal1"),
directions=("V", "H"))
m1m2 = factory.create(module_type="contact",
layer_stack=("metal1", "via1", "metal2"),
directions=("H", "V"))
m2m3 = factory.create(module_type="contact",
layer_stack=("metal2", "via2", "metal3"),
directions=("V", "H"))
if "metal4" in layer.keys():
m3m4 = factory.create(module_type="contact",
layer_stack=("metal3", "via3", "metal4"),
directions=("H", "V"))
else:
m3m4 = None

View File

@ -0,0 +1,134 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2020 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
class _pins:
def __init__(self, pin_dict):
# make the pins elements of the class to allow "." access.
# For example: props.bitcell.cell_6t.pin.bl = "foobar"
for k,v in pin_dict.items():
self.__dict__[k] = v
class _cell:
def __init__(self, pin_dict):
pin_dict.update(self._default_power_pins())
self._pins = _pins(pin_dict)
@property
def pin(self):
return self._pins
def _default_power_pins(self):
return { 'vdd' : 'vdd', 'gnd' : 'gnd' }
class _mirror_axis:
def __init__(self, x, y):
self.x = x
self.y = y
class _bitcell:
def __init__(self, mirror, split_wl, cell_6t, cell_1rw1r, cell_1w1r):
self.mirror = mirror
self.split_wl = split_wl
self._6t = cell_6t
self._1rw1r = cell_1rw1r
self._1w1r = cell_1w1r
def _default():
axis = _mirror_axis(True, False)
cell_6t = _cell({'bl' : 'bl',
'br' : 'br',
'wl' : 'wl'})
cell_1rw1r = _cell({'bl0' : 'bl0',
'br0' : 'br0',
'bl1' : 'bl1',
'br1' : 'br1',
'wl0' : 'wl0',
'wl1' : 'wl1'})
cell_1w1r = _cell({'bl0' : 'bl0',
'br0' : 'br0',
'bl1' : 'bl1',
'br1' : 'br1',
'wl0' : 'wl0',
'wl1' : 'wl1'})
return _bitcell(cell_6t=cell_6t,
cell_1rw1r=cell_1rw1r,
cell_1w1r=cell_1w1r,
split_wl = False,
mirror=axis)
@property
def cell_6t(self):
return self._6t
@property
def cell_1rw1r(self):
return self._1rw1r
@property
def cell_1w1r(self):
return self._1w1r
class _dff:
def __init__(self, use_custom_ports, custom_port_list, custom_type_list, clk_pin):
self.use_custom_ports = use_custom_ports
self.custom_port_list = custom_port_list
self.custom_type_list = custom_type_list
self.clk_pin = clk_pin
class _dff_buff:
def __init__(self, use_custom_ports, custom_buff_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports
self.buf_ports = custom_buff_ports
self.add_body_contacts = add_body_contacts
class _dff_buff_array:
def __init__(self, use_custom_ports, add_body_contacts):
self.use_custom_ports = use_custom_ports
self.add_body_contacts = add_body_contacts
class cell_properties():
"""
This contains meta information about the custom designed cells. For
instance, pin names, or the axis on which they need to be mirrored. These
can be overriden in the tech.py file.
"""
def __init__(self):
self.names = {}
self._bitcell = _bitcell._default()
self._dff = _dff(use_custom_ports = False,
custom_port_list = ["D", "Q", "clk", "vdd", "gnd"],
custom_type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"],
clk_pin= "clk")
self._dff_buff = _dff_buff(use_custom_ports = False,
custom_buff_ports = ["D", "qint", "clk", "vdd", "gnd"],
add_body_contacts = False)
self._dff_buff_array = _dff_buff_array(use_custom_ports = False,
add_body_contacts = False)
@property
def bitcell(self):
return self._bitcell
@property
def dff(self):
return self._dff
@property
def dff_buff(self):
return self._dff_buff
@property
def dff_buff_array(self):
return self._dff_buff_array

View File

@ -8,12 +8,13 @@
from hierarchy_design import hierarchy_design
import contact
from globals import OPTS
import re
class design(hierarchy_design):
"""
This is the same as the hierarchy_design class except it contains
some DRC constants and analytical models for other modules to reuse.
some DRC/layer constants and analytical models for other modules to reuse.
"""
@ -21,42 +22,130 @@ class design(hierarchy_design):
hierarchy_design.__init__(self, name)
self.setup_drc_constants()
self.setup_layer_constants()
self.setup_multiport_constants()
from tech import layer
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)
if "metal4" in layer:
self.m3_pitch = max(contact.m3m4.width, contact.m3m4.height) + max(self.m3_space, self.m4_space)
def setup_layer_constants(self):
"""
These are some layer constants used
in many places in the compiler.
"""
import tech
for key in dir(tech):
# Single layer width rules
match = re.match(r".*_stack$", key)
if match:
layer_stack = getattr(tech, key)
# Set the stack as a local helper
setattr(self, key, layer_stack)
# Add the pitch
setattr(self,
"{}_pitch".format(layer_stack[0]),
self.compute_pitch(layer_stack))
if False:
print("m1_pitch", self.m1_pitch)
print("m2_pitch", self.m2_pitch)
print("m3_pitch", self.m3_pitch)
import sys
sys.exit(1)
def compute_pitch(self, layer_stack):
"""
This is contact direction independent pitch,
i.e. we take the maximum contact dimension
"""
(layer1, via, layer2) = layer_stack
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
else:
self.m3_pitch = self.m2_pitch
contact1 = getattr(contact, layer1 + "_via")
max_contact = max(contact1.width, contact1.height)
layer1_space = getattr(self, layer1 + "_space")
layer2_space = getattr(self, layer2 + "_space")
pitch = max_contact + max(layer1_space, layer2_space)
return pitch
def setup_drc_constants(self):
""" These are some DRC constants used in many places in the compiler."""
from tech import drc, layer
self.well_width = drc("minwidth_well")
self.poly_width = drc("minwidth_poly")
self.poly_space = drc("poly_to_poly")
self.m1_width = drc("minwidth_metal1")
self.m1_space = drc("metal1_to_metal1")
self.m2_width = drc("minwidth_metal2")
self.m2_space = drc("metal2_to_metal2")
self.m3_width = drc("minwidth_metal3")
self.m3_space = drc("metal3_to_metal3")
if "metal4" in layer:
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_active_contact")
"""
These are some DRC constants used in many places
in the compiler.
"""
# Make some local rules for convenience
from tech import drc
for rule in drc.keys():
# Single layer width rules
match = re.search(r"minwidth_(.*)", rule)
if match:
if match.group(1) == "active_contact":
setattr(self, "contact_width", drc(match.group(0)))
else:
setattr(self, match.group(1) + "_width", drc(match.group(0)))
self.poly_to_active = drc("poly_to_active")
self.poly_extend_active = drc("poly_extend_active")
self.poly_to_poly_contact = drc("poly_to_poly_contact")
self.active_contact_to_gate = drc("active_contact_to_gate")
self.well_enclose_active = drc("well_enclosure_active")
self.implant_enclose_active = drc("implant_enclosure_active")
self.implant_space = drc("implant_to_implant")
# Single layer area rules
match = re.search(r"minarea_(.*)", rule)
if match:
setattr(self, match.group(0), drc(match.group(0)))
# Single layer spacing rules
match = re.search(r"(.*)_to_(.*)", rule)
if match and match.group(1) == match.group(2):
setattr(self, match.group(1) + "_space", drc(match.group(0)))
elif match and match.group(1) != match.group(2):
if match.group(2) == "poly_active":
setattr(self, match.group(1) + "_to_contact",
drc(match.group(0)))
else:
setattr(self, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_enclose_(.*)", rule)
if match:
setattr(self, match.group(0), drc(match.group(0)))
match = re.search(r"(.*)_extend_(.*)", rule)
if match:
setattr(self, match.group(0), drc(match.group(0)))
# Create the maximum well extend active that gets used
# by cells to extend the wells for interaction with other cells
from tech import layer
self.well_extend_active = 0
if "nwell" in layer:
self.well_extend_active = max(self.well_extend_active, self.nwell_extend_active)
if "pwell" in layer:
self.well_extend_active = max(self.well_extend_active, self.pwell_extend_active)
# These are for debugging previous manual rules
if False:
print("poly_width", self.poly_width)
print("poly_space", self.poly_space)
print("m1_width", self.m1_width)
print("m1_space", self.m1_space)
print("m2_width", self.m2_width)
print("m2_space", self.m2_space)
print("m3_width", self.m3_width)
print("m3_space", self.m3_space)
print("m4_width", self.m4_width)
print("m4_space", self.m4_space)
print("active_width", self.active_width)
print("active_space", self.active_space)
print("contact_width", self.contact_width)
print("poly_to_active", self.poly_to_active)
print("poly_extend_active", self.poly_extend_active)
print("poly_to_contact", self.poly_to_contact)
print("contact_to_gate", self.contact_to_gate)
print("well_enclose_active", self.well_enclose_active)
print("implant_enclose_active", self.implant_enclose_active)
print("implant_space", self.implant_space)
import sys
sys.exit(1)
def setup_multiport_constants(self):
"""

View File

@ -69,7 +69,9 @@ class geometry:
""" Transform with offset, mirror and rotation to get the absolute pin location.
We must then re-find the ll and ur. The master is the cell instance. """
if OPTS.netlist_only:
self.boundary = [vector(0,0), vector(0,0)]
return
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
if mirror == "MX":

File diff suppressed because it is too large Load Diff

View File

@ -22,30 +22,53 @@ class pin_layout:
self.name = name
# repack the rect as a vector, just in case
if type(rect[0]) == vector:
self.rect = rect
self._rect = rect
else:
self.rect = [vector(rect[0]), vector(rect[1])]
self._rect = [vector(rect[0]), vector(rect[1])]
# snap the rect to the grid
self.rect = [x.snap_to_grid() for x in self.rect]
self._rect = [x.snap_to_grid() for x in self.rect]
debug.check(self.width() > 0, "Zero width pin.")
debug.check(self.height() > 0, "Zero height pin.")
# if it's a string, use the name
if type(layer_name_pp) == str:
self.layer = layer_name_pp
self._layer = layer_name_pp
# else it is required to be a lpp
else:
for (layer_name, lpp) in layer.items():
if not lpp:
continue
if self.same_lpp(layer_name_pp, lpp):
self.layer = layer_name
self._layer = layer_name
break
else:
debug.error("Couldn't find layer {}".format(layer_name_pp), -1)
self.lpp = layer[self.layer]
self._recompute_hash()
@property
def layer(self):
return self._layer
@layer.setter
def layer(self, l):
self._layer = l
self._recompute_hash()
@property
def rect(self):
return self._rect
@rect.setter
def rect(self, r):
self._rect = r
self._recompute_hash()
def _recompute_hash(self):
""" Recompute the hash for our hash cache """
self._hash = hash(repr(self))
def __str__(self):
""" override print function output """
@ -64,8 +87,12 @@ class pin_layout:
self.rect[1])
def __hash__(self):
""" Implement the hash function for sets etc. """
return hash(repr(self))
"""
Implement the hash function for sets etc. We only return a cached
value, that is updated when either 'rect' or 'layer' are changed. This
is a major speedup, if pin_layout is used as a key for dicts.
"""
return self._hash
def __lt__(self, other):
""" Provide a function for ordering items by the ll point """
@ -101,7 +128,14 @@ class pin_layout:
max_y = max(max_y, pin.ur().y)
self.rect = [vector(min_x, min_y), vector(max_x, max_y)]
def fix_minarea(self):
"""
Try to fix minimum area rule.
"""
min_area = drc("{}_minarea".format(self.layer))
pass
def inflate(self, spacing=None):
"""
Inflate the rectangle by the spacing (or other rule)

View File

@ -96,10 +96,12 @@ def get_libcell_size(name, units, lpp):
Open a GDS file and return the library cell size from either the
bounding box or a border layer.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
return(get_gds_size(name, cell_gds, units, lpp))
def get_gds_pins(pin_names, name, gds_filename, units):
"""
Open a GDS file and find the pins in pin_names as text on a given layer.
@ -128,6 +130,7 @@ def get_libcell_pins(pin_list, name, units):
Open a GDS file and find the pins in pin_list as text on a given layer.
Return these as a rectangle layer pair for each pin.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
return(get_gds_pins(pin_list, name, cell_gds, units))

View File

@ -28,6 +28,7 @@ class vector():
else:
self.x = float(x)
self.y = float(y)
self._hash = hash((self.x,self.y))
def __str__(self):
""" override print function output """
@ -49,7 +50,7 @@ class vector():
else:
self.x=float(value[0])
self.y=float(value[1])
def __getitem__(self, index):
"""
override getitem function
@ -97,7 +98,7 @@ class vector():
Note: This assumes that you DON'T CHANGE THE VECTOR or it will
break things.
"""
return hash((self.x,self.y))
return self._hash
def snap_to_grid(self):
self.x = self.snap_offset_to_grid(self.x)

View File

@ -7,7 +7,8 @@
#
import debug
import utils
from tech import GDS, layer
from tech import GDS, layer, parameter
from tech import cell_properties as props
import bitcell_base
@ -19,9 +20,21 @@ class bitcell(bitcell_base.bitcell_base):
library.
"""
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
# If we have a split WL bitcell, if not be backwards
# compatible in the tech file
if props.bitcell.split_wl:
pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
else:
pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Qbar']
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("cell_6t",
GDS["unit"],
layer["boundary"])
@ -37,42 +50,47 @@ class bitcell(bitcell_base.bitcell_base):
self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl"]
if props.bitcell.split_wl:
row_pins = ["wl0", "wl1"]
else:
row_pins = [props.bitcell.cell_6t.pin.wl]
return row_pins
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl", "br"]
pin = props.bitcell.cell_6t.pin
column_pins = [pin.bl, pin.br]
return column_pins
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl"]
return column_pins
return [props.bitcell.cell_6t.pin.bl]
def get_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br"]
return column_pins
return [props.bitcell.cell_6t.pin.br]
def get_bl_name(self, port=0):
"""Get bl name"""
debug.check(port == 0, "One port for bitcell only.")
return "bl"
return props.bitcell.cell_6t.pin.bl
def get_br_name(self, port=0):
"""Get bl name"""
debug.check(port == 0, "One port for bitcell only.")
return "br"
return props.bitcell.cell_6t.pin.br
def get_wl_name(self, port=0):
"""Get wl name"""
debug.check(port == 0, "One port for bitcell only.")
return "wl"
if props.bitcell.split_wl:
return "wl{}".format(port)
else:
debug.check(port == 0, "One port for bitcell only.")
return props.bitcell.cell_6t.pin.wl
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.

View File

@ -8,6 +8,7 @@
import debug
import utils
from tech import GDS, layer, parameter, drc
from tech import cell_properties as props
import logical_effort
import bitcell_base
@ -20,7 +21,15 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base):
library.
"""
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
@ -39,85 +48,92 @@ class bitcell_1rw_1r(bitcell_base.bitcell_base):
self.pin_map = bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1rw_1r.pin_names
self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
"bl1_{0}".format(col),
"br1_{0}".format(col),
"wl0_{0}".format(row),
"wl1_{0}".format(row),
pin_name = props.bitcell.cell_1rw1r.pin
bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col),
"{0}_{1}".format(pin_name.br0, col),
"{0}_{1}".format(pin_name.bl1, col),
"{0}_{1}".format(pin_name.br1, col),
"{0}_{1}".format(pin_name.wl0, row),
"{0}_{1}".format(pin_name.wl1, row),
"vdd",
"gnd"]
return bitcell_pins
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
return row_pins
return [props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1]
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl0", "br0", "bl1", "br1"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1]
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl0", "bl1"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.bl1]
def get_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br0", "br1"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.br1]
def get_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = ["bl0", "bl1"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.bl1]
def get_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = ["br0", "br1"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.br1]
def get_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = ["bl0"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.bl0]
def get_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = ["br0"]
return column_pins
return [props.bitcell.cell_1rw1r.pin.br1]
def get_bl_name(self, port=0):
"""Get bl name by port"""
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "bl{}".format(port)
return self.bl_names[port]
def get_br_name(self, port=0):
"""Get bl name by port"""
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "br{}".format(port)
return self.br_names[port]
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
return self.wl_names[port]
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
# Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self)
graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self)
pins = props.bitcell.cell_1rw1r.pin
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self)
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)
# Port 1 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)

View File

@ -8,6 +8,7 @@
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -19,7 +20,14 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
library.
"""
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
pin_names = [props.bitcell.cell_1w1r.pin.bl0,
props.bitcell.cell_1w1r.pin.br0,
props.bitcell.cell_1w1r.pin.bl1,
props.bitcell.cell_1w1r.pin.br1,
props.bitcell.cell_1w1r.pin.wl0,
props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
@ -39,80 +47,88 @@ class bitcell_1w_1r(bitcell_base.bitcell_base):
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
pin_names = bitcell_1w_1r.pin_names
self.bl_names = [pin_names[0], pin_names[2]]
self.br_names = [pin_names[1], pin_names[3]]
self.wl_names = [pin_names[4], pin_names[5]]
def get_bitcell_pins(self, col, row):
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
"bl1_{0}".format(col),
"br1_{0}".format(col),
"wl0_{0}".format(row),
"wl1_{0}".format(row),
pin_name = props.bitcell.cell_1w1r.pin
bitcell_pins = ["{0}_{1}".format(pin_name.bl0, col),
"{0}_{1}".format(pin_name.br0, col),
"{0}_{1}".format(pin_name.bl1, col),
"{0}_{1}".format(pin_name.br1, col),
"{0}_{1}".format(pin_name.wl0, row),
"{0}_{1}".format(pin_name.wl1, row),
"vdd",
"gnd"]
return bitcell_pins
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
return row_pins
return [props.bitcell.cell_1w1r.pin.wl0,
props.bitcell.cell_1w1r.pin.wl1]
def get_all_bitline_names(self):
""" Creates a list of all bitline pin names (both bl and br) """
column_pins = ["bl0", "br0", "bl1", "br1"]
return column_pins
return [props.bitcell.cell_1w1r.pin.bl0,
props.bitcell.cell_1w1r.pin.br0,
props.bitcell.cell_1w1r.pin.bl1,
props.bitcell.cell_1w1r.pin.br1]
def get_all_bl_names(self):
""" Creates a list of all bl pins names """
column_pins = ["bl0", "bl1"]
return column_pins
return [props.bitcell.cell_1w1r.pin.bl0,
props.bitcell.cell_1w1r.pin.bl1]
def get_all_br_names(self):
""" Creates a list of all br pins names """
column_pins = ["br0", "br1"]
return column_pins
return [props.bitcell.cell_1w1r.pin.br0,
props.bitcell.cell_1w1r.pin.br1]
def get_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = ["bl0", "bl1"]
return column_pins
return [props.bitcell.cell_1w1r.pin.bl0,
props.bitcell.cell_1w1r.pin.bl1]
def get_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = ["br0", "br1"]
return column_pins
return [props.bitcell.cell_1w1r.pin.br0,
props.bitcell.cell_1w1r.pin.br1]
def get_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = ["bl0"]
return column_pins
return [props.bitcell.cell_1w1r.pin.bl0]
def get_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = ["br0"]
return column_pins
return [props.bitcell.cell_1w1r.pin.br1]
def get_bl_name(self, port=0):
"""Get bl name by port"""
return "bl{}".format(port)
return self.bl_names[port]
def get_br_name(self, port=0):
"""Get bl name by port"""
return "br{}".format(port)
return self.br_names[port]
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
return self.wl_names[port]
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1w1r.pin
# Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)
# Port 1 is a write port, so its timing is not considered here.

View File

@ -8,6 +8,7 @@
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -18,8 +19,12 @@ class dummy_bitcell(bitcell_base.bitcell_base):
the layout and netlist should be available in the technology
library.
"""
pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
(width, height) = utils.get_libcell_size("dummy_cell_6t",
GDS["unit"],
layer["boundary"])

View File

@ -8,6 +8,7 @@
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -18,7 +19,15 @@ class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",

View File

@ -8,6 +8,7 @@
import debug
import utils
from tech import GDS, layer
from tech import cell_properties as props
import bitcell_base
@ -18,7 +19,14 @@ class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
pin_names = [props.bitcell.cell_1w1r.pin.bl0,
props.bitcell.cell_1w1r.pin.br0,
props.bitcell.cell_1w1r.pin.bl1,
props.bitcell.cell_1w1r.pin.br1,
props.bitcell.cell_1w1r.pin.wl0,
props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1w_1r",

View File

@ -199,7 +199,6 @@ class pbitcell(bitcell_base.bitcell_base):
def calculate_spacing(self):
""" Calculate transistor spacings """
# calculate metal contact extensions over transistor active
readwrite_nmos_contact_extension = 0.5 * \
(self.readwrite_nmos.active_contact.height - self.readwrite_nmos.active_height)
@ -213,20 +212,20 @@ class pbitcell(bitcell_base.bitcell_base):
# y-offset for the access transistor's gate contact
self.gate_contact_yoffset = max_contact_extension + self.m2_space \
+ 0.5 * max(contact.poly.height, contact.m1m2.height)
+ 0.5 * max(contact.poly_contact.height, contact.m1_via.height)
# y-position of access transistors
self.port_ypos = self.m1_space + 0.5 * contact.m1m2.height + self.gate_contact_yoffset
self.port_ypos = self.m1_space + 0.5 * contact.m1_via.height + self.gate_contact_yoffset
# y-position of inverter nmos
self.inverter_nmos_ypos = self.port_ypos
# spacing between ports (same for read/write and write ports)
self.bitline_offset = -0.5 * self.readwrite_nmos.active_width \
+ 0.5 * contact.m1m2.height \
+ 0.5 * contact.m1_via.height \
+ self.m2_space + self.m2_width
m2_constraint = self.bitline_offset + self.m2_space \
+ 0.5 * contact.m1m2.height \
+ 0.5 * contact.m1_via.height \
- 0.5 * self.readwrite_nmos.active_width
self.write_port_spacing = max(self.active_space,
self.m1_space,
@ -234,7 +233,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.read_port_spacing = self.bitline_offset + self.m2_space
# spacing between cross coupled inverters
self.inverter_to_inverter_spacing = contact.poly.width + self.m1_space
self.inverter_to_inverter_spacing = contact.poly_contact.width + self.m1_space
# calculations related to inverter connections
inverter_pmos_contact_extension = 0.5 * \
@ -243,19 +242,19 @@ class pbitcell(bitcell_base.bitcell_base):
(self.inverter_nmos.active_contact.height - self.inverter_nmos.active_height)
self.inverter_gap = max(self.poly_to_active,
self.m1_space + inverter_nmos_contact_extension) \
+ self.poly_to_poly_contact + 2 * contact.poly.width \
+ self.poly_to_contact + 2 * contact.poly_contact.width \
+ self.m1_space + inverter_pmos_contact_extension
self.cross_couple_lower_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \
+ max(self.poly_to_active,
self.m1_space + inverter_nmos_contact_extension) \
+ 0.5 * contact.poly.width
+ 0.5 * contact.poly_contact.width
self.cross_couple_upper_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \
+ max(self.poly_to_active,
self.m1_space + inverter_nmos_contact_extension) \
+ self.poly_to_poly_contact \
+ 1.5 * contact.poly.width
+ self.poly_to_contact \
+ 1.5 * contact.poly_contact.width
# spacing between wordlines (and gnd)
self.m1_offset = -0.5 * self.m1_width
@ -263,7 +262,7 @@ class pbitcell(bitcell_base.bitcell_base):
# spacing for vdd
implant_constraint = max(inverter_pmos_contact_extension, 0) \
+ 2 * self.implant_enclose_active \
+ 0.5 * (contact.well.width - self.m1_width)
+ 0.5*(self.inverter_pmos.active_contact.height - self.m1_width)
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space
self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width
@ -291,7 +290,7 @@ class pbitcell(bitcell_base.bitcell_base):
(self.write_nmos.active_width + self.write_port_spacing) \
- self.num_r_ports * \
(self.read_port_width + self.read_port_spacing) \
- self.bitline_offset - 0.5 * contact.m1m2.width
- self.bitline_offset - 0.5 * contact.m1_via.width
self.width = -2 * self.leftmost_xpos
self.height = self.topmost_ypos - self.botmost_ypos
@ -364,31 +363,29 @@ class pbitcell(bitcell_base.bitcell_base):
self.inverter_pmos_right.get_pin("G").bc()])
# connect output (drain/source) of inverters
self.add_path("metal1",
self.add_path("m1",
[self.inverter_nmos_left.get_pin("D").uc(),
self.inverter_pmos_left.get_pin("D").bc()],
width=contact.active.second_layer_width)
self.add_path("metal1",
width=self.inverter_nmos_left.get_pin("D").width())
self.add_path("m1",
[self.inverter_nmos_right.get_pin("S").uc(),
self.inverter_pmos_right.get_pin("S").bc()],
width=contact.active.second_layer_width)
width=self.inverter_nmos_right.get_pin("S").width())
# add contacts to connect gate poly to drain/source
# metal1 (to connect Q to Q_bar)
contact_offset_left = vector(self.inverter_nmos_left.get_pin("D").rc().x \
+ 0.5 * contact.poly.height,
+ 0.5 * contact.poly_contact.height,
self.cross_couple_upper_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset_left,
directions=("H", "H"))
self.add_via_center(layers=self.poly_stack,
offset=contact_offset_left)
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
- 0.5*contact.poly.height,
- 0.5*contact.poly_contact.height,
self.cross_couple_lower_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset_right,
directions=("H", "H"))
self.add_via_center(layers=self.poly_stack,
offset=contact_offset_right)
# connect contacts to gate poly (cross couple connections)
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x,
@ -404,7 +401,7 @@ class pbitcell(bitcell_base.bitcell_base):
# Add rails for vdd and gnd
gnd_ypos = self.m1_offset - self.total_ports * self.m1_pitch
self.gnd_position = vector(0, gnd_ypos)
self.add_rect_center(layer="metal1",
self.add_rect_center(layer="m1",
offset=self.gnd_position,
width=self.width)
self.add_power_pin("gnd", vector(0, gnd_ypos))
@ -416,7 +413,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ self.inverter_pmos.active_height \
+ self.vdd_offset
self.vdd_position = vector(0, vdd_ypos)
self.add_rect_center(layer="metal1",
self.add_rect_center(layer="m1",
offset=self.vdd_position,
width=self.width)
self.add_power_pin("vdd", vector(0, vdd_ypos))
@ -489,7 +486,7 @@ class pbitcell(bitcell_base.bitcell_base):
rwwl_ypos = self.m1_offset - k * self.m1_pitch
self.rwwl_positions[k] = vector(0, rwwl_ypos)
self.add_layout_pin_rect_center(text=self.rw_wl_names[k],
layer="metal1",
layer="m1",
offset=self.rwwl_positions[k],
width=self.width)
@ -499,7 +496,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ 0.5 * self.m2_width
self.rwbl_positions[k] = vector(rwbl_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.rw_bl_names[k],
layer="metal2",
layer="m2",
offset=self.rwbl_positions[k],
height=self.height)
@ -509,7 +506,7 @@ class pbitcell(bitcell_base.bitcell_base):
- 0.5 * self.m2_width
self.rwbr_positions[k] = vector(rwbr_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.rw_br_names[k],
layer="metal2",
layer="m2",
offset=self.rwbr_positions[k],
height=self.height)
@ -586,7 +583,7 @@ class pbitcell(bitcell_base.bitcell_base):
- k * self.m1_pitch
self.wwl_positions[k] = vector(0, wwl_ypos)
self.add_layout_pin_rect_center(text=self.w_wl_names[k],
layer="metal1",
layer="m1",
offset=self.wwl_positions[k],
width=self.width)
@ -596,7 +593,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ 0.5 * self.m2_width
self.wbl_positions[k] = vector(wbl_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.w_bl_names[k],
layer="metal2",
layer="m2",
offset=self.wbl_positions[k],
height=self.height)
@ -606,7 +603,7 @@ class pbitcell(bitcell_base.bitcell_base):
- 0.5 * self.m2_width
self.wbr_positions[k] = vector(wbr_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.w_br_names[k],
layer="metal2",
layer="m2",
offset=self.wbr_positions[k],
height=self.height)
@ -713,7 +710,7 @@ class pbitcell(bitcell_base.bitcell_base):
- k * self.m1_pitch
self.rwl_positions[k] = vector(0, rwl_ypos)
self.add_layout_pin_rect_center(text=self.r_wl_names[k],
layer="metal1",
layer="m1",
offset=self.rwl_positions[k],
width=self.width)
@ -723,7 +720,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ 0.5 * self.m2_width
self.rbl_positions[k] = vector(rbl_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.r_bl_names[k],
layer="metal2",
layer="m2",
offset=self.rbl_positions[k],
height=self.height)
@ -733,7 +730,7 @@ class pbitcell(bitcell_base.bitcell_base):
- 0.5 * self.m2_width
self.rbr_positions[k] = vector(rbr_xpos, self.center_ypos)
self.add_layout_pin_rect_center(text=self.r_br_names[k],
layer="metal2",
layer="m2",
offset=self.rbr_positions[k],
height=self.height)
@ -771,25 +768,24 @@ class pbitcell(bitcell_base.bitcell_base):
# first transistor on either side of the cross coupled inverters
# does not need to route to wordline on metal2
if (k == 0) or (k == 1):
self.add_via_center(layers=("poly", "contact", "metal1"),
self.add_via_center(layers=self.poly_stack,
offset=port_contact_offset)
self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("metal1",
self.add_path("m1",
[port_contact_offset, wl_contact_offset])
else:
self.add_via_center(layers=("poly", "contact", "metal1"),
self.add_via_center(layers=self.poly_stack,
offset=port_contact_offset)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=port_contact_offset)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=wl_contact_offset,
directions=("H", "H"))
self.add_via_center(layers=self.m1_stack,
offset=wl_contact_offset)
self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("metal2",
self.add_path("m2",
[port_contact_offset, wl_contact_offset])
def route_bitlines(self):
@ -824,11 +820,11 @@ class pbitcell(bitcell_base.bitcell_base):
# Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell:
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=port_contact_offest)
self.add_path("metal2",
[port_contact_offest, bl_offset], width=contact.m1m2.height)
self.add_path("m2",
[port_contact_offest, bl_offset], width=contact.m1_via.height)
for k in range(self.total_ports):
port_contact_offest = right_port_transistors[k].get_pin("D").center()
@ -836,11 +832,11 @@ class pbitcell(bitcell_base.bitcell_base):
# Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell:
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=port_contact_offest)
self.add_path("metal2",
[port_contact_offest, br_offset], width=contact.m1m2.height)
self.add_path("m2",
[port_contact_offest, br_offset], width=contact.m1_via.height)
def route_supply(self):
""" Route inverter nmos and read-access nmos to gnd. Route inverter pmos to vdd. """
@ -853,30 +849,29 @@ class pbitcell(bitcell_base.bitcell_base):
nmos_contact_positions.append(self.read_access_nmos_right[k].get_pin("S").center())
for position in nmos_contact_positions:
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=position)
if position.x > 0:
contact_correct = 0.5 * contact.m1m2.height
contact_correct = 0.5 * contact.m1_via.height
else:
contact_correct = -0.5 * contact.m1m2.height
contact_correct = -0.5 * contact.m1_via.height
supply_offset = vector(position.x + contact_correct,
self.gnd_position.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=supply_offset,
directions=("H", "H"))
self.add_via_center(layers=self.m1_stack,
offset=supply_offset)
self.add_path("metal2", [position, supply_offset])
self.add_path("m2", [position, supply_offset])
# route inverter pmos to vdd
vdd_pos_left = vector(self.inverter_nmos_left.get_pin("S").uc().x,
self.vdd_position.y)
self.add_path("metal1",
self.add_path("m1",
[self.inverter_pmos_left.get_pin("S").uc(), vdd_pos_left])
vdd_pos_right = vector(self.inverter_nmos_right.get_pin("D").uc().x,
self.vdd_position.y)
self.add_path("metal1",
self.add_path("m1",
[self.inverter_pmos_right.get_pin("D").uc(), vdd_pos_right])
def route_readwrite_access(self):
@ -889,14 +884,14 @@ class pbitcell(bitcell_base.bitcell_base):
self.cross_couple_lower_ypos)
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
self.add_path("m1",
[self.readwrite_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
mid = vector(self.readwrite_nmos_right[k].get_pin("S").uc().x,
self.cross_couple_lower_ypos)
Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
self.add_path("m1",
[self.readwrite_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
def route_write_access(self):
@ -909,14 +904,14 @@ class pbitcell(bitcell_base.bitcell_base):
self.cross_couple_lower_ypos)
Q_pos = vector(self.inverter_nmos_left.get_pin("D").lx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
self.add_path("m1",
[self.write_nmos_left[k].get_pin("D").uc(), mid, Q_pos])
mid = vector(self.write_nmos_right[k].get_pin("S").uc().x,
self.cross_couple_lower_ypos)
Q_bar_pos = vector(self.inverter_nmos_right.get_pin("S").rx(),
self.cross_couple_lower_ypos)
self.add_path("metal1",
self.add_path("m1",
[self.write_nmos_right[k].get_pin("S").uc(), mid, Q_bar_pos])
def route_read_access(self):
@ -926,18 +921,16 @@ class pbitcell(bitcell_base.bitcell_base):
"""
# add poly to metal1 contacts for gates of the inverters
left_storage_contact = vector(self.inverter_nmos_left.get_pin("G").lc().x \
- self.poly_to_poly_contact - 0.5*contact.poly.width,
- self.poly_to_contact - 0.5*contact.poly_contact.width,
self.cross_couple_upper_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=left_storage_contact,
directions=("H", "H"))
self.add_via_center(layers=self.poly_stack,
offset=left_storage_contact)
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
+ self.poly_to_poly_contact + 0.5*contact.poly.width,
+ self.poly_to_contact + 0.5*contact.poly_contact.width,
self.cross_couple_upper_ypos)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=right_storage_contact,
directions=("H", "H"))
self.add_via_center(layers=self.poly_stack,
offset=right_storage_contact)
inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos)
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left])
@ -952,7 +945,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ vector(0,
self.gate_contact_yoffset - self.poly_extend_active)
self.add_via_center(layers=("poly", "contact", "metal1"),
self.add_via_center(layers=self.poly_stack,
offset=port_contact_offset)
self.add_path("poly",
@ -960,14 +953,14 @@ class pbitcell(bitcell_base.bitcell_base):
mid = vector(self.read_access_nmos_left[k].get_pin("G").uc().x,
self.cross_couple_upper_ypos)
self.add_path("metal1",
self.add_path("m1",
[port_contact_offset, mid, left_storage_contact])
port_contact_offset = self.read_access_nmos_right[k].get_pin("G").uc() \
+ vector(0,
self.gate_contact_yoffset - self.poly_extend_active)
self.add_via_center(layers=("poly", "contact", "metal1"),
self.add_via_center(layers=self.poly_stack,
offset=port_contact_offset)
self.add_path("poly",
@ -975,7 +968,7 @@ class pbitcell(bitcell_base.bitcell_base):
mid = vector(self.read_access_nmos_right[k].get_pin("G").uc().x,
self.cross_couple_upper_ypos)
self.add_path("metal1",
self.add_path("m1",
[port_contact_offset, mid, right_storage_contact])
def extend_well(self):
@ -984,33 +977,36 @@ class pbitcell(bitcell_base.bitcell_base):
"""
# extend pwell to encompass entire nmos region of the cell up to the
# height of the tallest nmos transistor
max_nmos_well_height = max(self.inverter_nmos.cell_well_height,
self.readwrite_nmos.cell_well_height,
self.write_nmos.cell_well_height,
self.read_nmos.cell_well_height)
max_nmos_well_height = max(self.inverter_nmos.well_height,
self.readwrite_nmos.well_height,
self.write_nmos.well_height,
self.read_nmos.well_height)
well_height = max_nmos_well_height + self.port_ypos \
- self.well_enclose_active - self.gnd_position.y
offset = vector(self.leftmost_xpos, self.botmost_ypos)
- self.nwell_enclose_active - self.gnd_position.y
# FIXME fudge factor xpos
well_width = self.width + 2*self.nwell_enclose_active
offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos)
self.add_rect(layer="pwell",
offset=offset,
width=self.width,
width=well_width,
height=well_height)
# extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
- self.well_enclose_active
- self.nwell_enclose_active
inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \
+ self.inverter_gap - self.well_enclose_active
+ self.inverter_gap - self.nwell_enclose_active
# calculate width of the two combined nwells
# calculate height to encompass nimplant connected to vdd
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \
+ 2 * self.well_enclose_active
+ 2 * self.nwell_enclose_active
well_height = self.vdd_position.y - inverter_well_ypos \
+ self.well_enclose_active + drc["minwidth_tx"]
+ self.nwell_enclose_active + drc["minwidth_tx"]
offset = [inverter_well_xpos, inverter_well_ypos]
# FIXME fudge factor xpos
offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos]
self.add_rect(layer="nwell",
offset=offset,
width=well_width,
@ -1019,7 +1015,7 @@ class pbitcell(bitcell_base.bitcell_base):
# add well contacts
# connect pimplants to gnd
offset = vector(0, self.gnd_position.y)
self.add_via_center(layers=("active", "contact", "metal1"),
self.add_via_center(layers=self.active_stack,
offset=offset,
directions=("H", "H"),
implant_type="p",
@ -1027,7 +1023,7 @@ class pbitcell(bitcell_base.bitcell_base):
# connect nimplants to vdd
offset = vector(0, self.vdd_position.y)
self.add_via_center(layers=("active", "contact", "metal1"),
self.add_via_center(layers=self.active_stack,
offset=offset,
directions=("H", "H"),
implant_type="n",
@ -1076,7 +1072,7 @@ class pbitcell(bitcell_base.bitcell_base):
"""
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
vdd_pos = self.inverter_pmos_right.get_pin("D").center()
self.add_path("metal1", [Q_bar_pos, vdd_pos])
self.add_path("m1", [Q_bar_pos, vdd_pos])
def get_storage_net_names(self):
"""

View File

@ -8,7 +8,10 @@
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
from tech import GDS,layer,drc,parameter,cell_properties
from tech import cell_properties as props
from globals import OPTS
class replica_bitcell(design.design):
"""
@ -17,10 +20,24 @@ class replica_bitcell(design.design):
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
if cell_properties.bitcell.split_wl:
pin_names = ["bl", "br", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT" , "POWER", "GROUND"]
else:
pin_names = [props.bitcell.cell_6t.pin.bl,
props.bitcell.cell_6t.pin.br,
props.bitcell.cell_6t.pin.wl,
props.bitcell.cell_6t.pin.vdd,
props.bitcell.cell_6t.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("replica_cell_6t", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_6t", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
def __init__(self, name=""):
# Ignore the name argument
@ -56,4 +73,4 @@ class replica_bitcell(design.design):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -9,6 +9,7 @@ import design
import debug
import utils
from tech import GDS,layer,drc,parameter
from tech import cell_properties as props
class replica_bitcell_1rw_1r(design.design):
"""
@ -17,7 +18,15 @@ class replica_bitcell_1rw_1r(design.design):
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
pin_names = [props.bitcell.cell_1rw1r.pin.bl0,
props.bitcell.cell_1rw1r.pin.br0,
props.bitcell.cell_1rw1r.pin.bl1,
props.bitcell.cell_1rw1r.pin.br1,
props.bitcell.cell_1rw1r.pin.wl0,
props.bitcell.cell_1rw1r.pin.wl1,
props.bitcell.cell_1rw1r.pin.vdd,
props.bitcell.cell_1rw1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1rw_1r", GDS["unit"])
@ -47,14 +56,15 @@ class replica_bitcell_1rw_1r(design.design):
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1rw1r.pin
#Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self)
graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self)
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.bl0], self)
graph.add_edge(pin_dict[pins.wl0], pin_dict[pins.br0], self)
# Port 1 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)

View File

@ -9,6 +9,7 @@ import design
import debug
import utils
from tech import GDS,layer,drc,parameter
from tech import cell_properties as props
class replica_bitcell_1w_1r(design.design):
"""
@ -17,7 +18,15 @@ class replica_bitcell_1w_1r(design.design):
is a hand-made cell, so the layout and netlist should be available in
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
pin_names = [props.bitcell.cell_1w1r.pin.bl0,
props.bitcell.cell_1w1r.pin.br0,
props.bitcell.cell_1w1r.pin.bl1,
props.bitcell.cell_1w1r.pin.br1,
props.bitcell.cell_1w1r.pin.wl0,
props.bitcell.cell_1w1r.pin.wl1,
props.bitcell.cell_1w1r.pin.vdd,
props.bitcell.cell_1w1r.pin.gnd]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("replica_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "replica_cell_1w_1r", GDS["unit"])
@ -47,13 +56,14 @@ class replica_bitcell_1w_1r(design.design):
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
debug.info(1,'Adding edges for {}'.format(inst_name))
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
pins = props.bitcell.cell_1w1r.pin
#Edges hardcoded here. Essentially wl->bl/br for the read port.
# Port 1 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
# Port 0 is a write port, so its timing is not considered here.
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.bl1], self)
graph.add_edge(pin_dict[pins.wl1], pin_dict[pins.br1], self)
# Port 0 is a write port, so its timing is not considered here.

View File

@ -180,30 +180,30 @@ class delay(simulation):
def create_read_bit_measures(self):
""" Adds bit measurements for read0 and read1 cycles """
self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
meas.meta_str = cycle
self.bit_meas[polarity].append(meas)
self.read_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.bit_meas.values() for meas in meas_list]
return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list]
def create_write_bit_measures(self):
""" Adds bit measurements for write0 and write1 cycles """
self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]}
meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE)
for cycle in meas_cycles:
meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name)
single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data)
for polarity,meas in single_bit_meas.items():
meas.meta_str = cycle
self.bit_meas[polarity].append(meas)
self.write_bit_meas[polarity].append(meas)
# Dictionary values are lists, reduce to a single list of measurements
return [meas for meas_list in self.bit_meas.values() for meas in meas_list]
return [meas for meas_list in self.write_bit_meas.values() for meas in meas_list]
def get_bit_measures(self, meas_tag, probe_address, probe_data):
"""
@ -649,8 +649,9 @@ class delay(simulation):
if (time_out <= 0):
debug.error("Timed out, could not find a feasible period.",2)
# Clear any write target ports and set read port
self.targ_write_ports = [port]
# Write ports are assumed non-critical to timing, so the first available is used
self.targ_write_ports = [self.write_ports[0]]
# Set target read port for simulation
self.targ_read_ports = [port]
debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port))
@ -733,7 +734,8 @@ class delay(simulation):
# First, check that the memory has the right values at the right times
if not self.check_bit_measures():
if not self.check_bit_measures(self.read_bit_meas) or \
not self.check_bit_measures(self.write_bit_meas):
return(False,{})
for port in self.targ_write_ports:
@ -824,13 +826,13 @@ class delay(simulation):
return dout_success
def check_bit_measures(self):
def check_bit_measures(self, bit_measures):
"""
Checks the measurements which represent the internal storage voltages
at the end of the read cycle.
"""
success = False
for polarity, meas_list in self.bit_meas.items():
for polarity, meas_list in bit_measures.items():
for meas in meas_list:
val = meas.retrieve_measure()
debug.info(2,"{}={}".format(meas.name, val))
@ -965,7 +967,8 @@ class delay(simulation):
# Binary search algorithm to find the min period (max frequency) of input port
time_out = 25
self.targ_write_ports = [port]
# Write ports are assumed non-critical to timing, so the first available is used
self.targ_write_ports = [self.write_ports[0]]
self.targ_read_ports = [port]
while True:
time_out -= 1
@ -1254,8 +1257,8 @@ class delay(simulation):
"""
# Using this requires setting at least one port to target for simulation.
if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0:
debug.error("No port selected for characterization.",1)
if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0:
debug.error("Write and read port must be specified for characterization.",1)
self.set_stimulus_variables()
# Get any available read/write port in case only a single write or read ports is being characterized.

View File

@ -81,17 +81,25 @@ class lib:
self.lib_files = []
# Nominal corner
self.add_corner(nom_process, nom_supply, nom_temperature)
corner_set = set()
nom_corner = (nom_process, nom_supply, nom_temperature)
corner_set.add(nom_corner)
if not OPTS.nominal_corner_only:
# Temperature corners
self.add_corner(nom_process, nom_supply, min_temperature)
self.add_corner(nom_process, nom_supply, max_temperature)
corner_set.add((nom_process, nom_supply, min_temperature))
corner_set.add((nom_process, nom_supply, max_temperature))
# Supply corners
self.add_corner(nom_process, min_supply, nom_temperature)
self.add_corner(nom_process, max_supply, nom_temperature)
corner_set.add((nom_process, min_supply, nom_temperature))
corner_set.add((nom_process, max_supply, nom_temperature))
# Process corners
self.add_corner(min_process, nom_supply, nom_temperature)
self.add_corner(max_process, nom_supply, nom_temperature)
corner_set.add((min_process, nom_supply, nom_temperature))
corner_set.add((max_process, nom_supply, nom_temperature))
# Enforce that nominal corner is the first to be characterized
self.add_corner(*nom_corner)
corner_set.remove(nom_corner)
for corner_tuple in corner_set:
self.add_corner(*corner_tuple)
def add_corner(self, proc, volt, temp):
self.corner_name = "{0}_{1}_{2}V_{3}C".format(self.sram.name,

View File

@ -9,7 +9,7 @@ import debug
from drc_value import *
from drc_lut import *
class design_rules():
class design_rules(dict):
"""
This is a class that implements the design rules structures.
"""
@ -43,6 +43,22 @@ class design_rules():
else:
debug.error("Must call complex DRC rule {} with arguments.".format(b),-1)
def keys(self):
return self.rules.keys()
def add_layer(self, name, width, spacing, area=0):
# Minimum width
self.add("minwidth_{}".format(name), width)
# Minimum spacing (could be a table too)
self.add("{0}_to_{0}".format(name), spacing)
# Minimum area
self.add("minarea_{}".format(name), area)
def add_enclosure(self, name, layer, enclosure, extension=None):
self.add("{0}_enclose_{1}".format(name, layer), enclosure)
# Reserved for asymmetric enclosures
if extension:
self.add("{0}_extend_{1}".format(name, layer), extension)
else:
self.add("{0}_extend_{1}".format(name, layer), enclosure)

View File

@ -19,6 +19,3 @@ output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"
pex_name = "magic"

View File

@ -12,14 +12,14 @@ class Gds2reader:
self.fileHandle = None
self.layoutObject = layoutObject
self.debugToTerminal=debugToTerminal
#do we dump debug data to the screen
def print64AsBinary(self,number):
for index in range(0,64):
print((number>>(63-index))&0x1,eol='')
print("\n")
def stripNonASCII(self,bytestring):
string = bytestring.decode('utf-8')
return string
@ -29,20 +29,20 @@ class Gds2reader:
#(1)sign (7)exponent (56)mantissa
#exponent is excess 64, mantissa has no implied 1
#a normal IEEE double is like this:
#(1)sign (11)exponent (52)mantissa
#(1)sign (11)exponent (52)mantissa
data = struct.unpack('>q',ibmData)[0]
sign = (data >> 63)&0x01
exponent = (data >> 56) & 0x7f
mantissa = data<<8 #chop off sign and exponent
if mantissa == 0:
newFloat = 0.0
else:
else:
exponent = ((exponent-64)*4)+1023 #convert to double exponent
#re normalize
while mantissa & 0x8000000000000000 == 0:
while mantissa & 0x8000000000000000 == 0:
mantissa<<=1
exponent-=1
exponent-=1
mantissa<<=1 #remove the assumed high bit
exponent-=1
#check for underflow error -- should handle these properly!
@ -56,7 +56,7 @@ class Gds2reader:
#convert back to double
newFloat = struct.unpack('>d',asciiDouble)[0]
return newFloat
def ieeeFloatCheck(self,aFloat):
asciiDouble = struct.pack('>d',aFloat)
data = struct.unpack('>q',asciiDouble)[0]
@ -70,12 +70,12 @@ class Gds2reader:
asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12))
newFloat = struct.unpack('>d',asciiDouble)[0]
print("Check:"+str(newFloat))
def readNextRecord(self):
global offset
recordLengthAscii = self.fileHandle.read(2) #first 2 bytes tell us the length of the record
if len(recordLengthAscii)==0:
return
return
recordLength = struct.unpack(">h",recordLengthAscii) #gives us a tuple with a short int inside
offset_int = int(recordLength[0]) # extract length
offset += offset_int # count offset
@ -96,20 +96,20 @@ class Gds2reader:
else:
print("Invalid GDSII Header")
return -1
#read records until we hit the UNITS section... this is the last part of the header
while 1:
record = self.readNextRecord()
idBits = record[0:2]
## Modified Date
if idBits==b'\x01\x02' and len(record)==26:
modYear = struct.unpack(">h",record[2:4])[0]
modYear = struct.unpack(">h",record[2:4])[0]
modMonth = struct.unpack(">h",record[4:6])[0]
modDay = struct.unpack(">h",record[6:8])[0]
modHour = struct.unpack(">h",record[8:10])[0]
modMinute = struct.unpack(">h",record[10:12])[0]
modSecond = struct.unpack(">h",record[12:14])[0]
lastAccessYear = struct.unpack(">h",record[14:16])[0]
lastAccessYear = struct.unpack(">h",record[14:16])[0]
lastAccessMonth = struct.unpack(">h",record[16:18])[0]
lastAccessDay = struct.unpack(">h",record[18:20])[0]
lastAccessHour = struct.unpack(">h",record[20:22])[0]
@ -169,14 +169,14 @@ class Gds2reader:
if(self.debugToTerminal==1):
print("Units: 1 user unit="+str(userUnits)+" database units, 1 database unit="+str(dbUnits)+" meters.")
break;
if(self.debugToTerminal==1):
if(self.debugToTerminal==1):
print("End of GDSII Header Found")
return 1
def readBoundary(self):
##reads in a boundary type structure = a filled polygon
if(self.debugToTerminal==1):
print("\t\t\tBeginBoundary")
print("\t\t\tBeginBoundary")
thisBoundary=GdsBoundary()
while 1:
record = self.readNextRecord()
@ -198,9 +198,9 @@ class Gds2reader:
self.layoutObject.layerNumbersInUse += [drawingLayer]
if(self.debugToTerminal==1):
print("\t\tDrawing Layer: "+str(drawingLayer))
elif(idBits==b'\x16\x02'): #Purpose
elif(idBits==b'\x0E\x02'): #Purpose DATATYPE
purposeLayer = struct.unpack(">h",record[2:4])[0]
thisBoundary.purposeLayer=purposeLayer
thisBoundary.purposeLayer=purposeLayer
if(self.debugToTerminal==1):
print("\t\tPurpose Layer: "+str(purposeLayer))
elif(idBits==b'\x10\x03'): #XY Data Points
@ -217,11 +217,11 @@ class Gds2reader:
print("\t\t\tEndBoundary")
break;
return thisBoundary
def readPath(self): #reads in a path structure
if(self.debugToTerminal==1):
print("\t\t\tBeginPath")
thisPath=GdsPath()
while 1:
record = self.readNextRecord()
@ -245,7 +245,7 @@ class Gds2reader:
print("\t\t\tDrawing Layer: "+str(drawingLayer))
elif(idBits==b'\x16\x02'): #Purpose
purposeLayer = struct.unpack(">h",record[2:4])[0]
thisPath.purposeLayer=purposeLayer
thisPath.purposeLayer=purposeLayer
if(self.debugToTerminal==1):
print("\t\tPurpose Layer: "+str(purposeLayer))
elif(idBits==b'\x21\x02'): #Path type
@ -253,6 +253,11 @@ class Gds2reader:
thisPath.pathType=pathType
if(self.debugToTerminal==1):
print("\t\t\tPath Type: "+str(pathType))
elif(idBits==b'\x0E\x02'): #Data type
dataType = struct.unpack(">h",record[2:4])[0]
thisPath.dataType=dataType
if(self.debugToTerminal==1):
print("\t\t\tData Type: "+str(dataType))
elif(idBits==b'\x0F\x03'): #Path width
pathWidth = struct.unpack(">i",record[2:6])[0]
thisPath.pathWidth=pathWidth
@ -272,7 +277,7 @@ class Gds2reader:
print("\t\t\tEndPath")
break;
return thisPath
def readSref(self): #reads in a reference to another structure
if(self.debugToTerminal==1):
print("\t\t\tBeginSref")
@ -313,7 +318,7 @@ class Gds2reader:
print("\t\t\tMagnification:"+str(magFactor))
elif(idBits==b'\x1C\x05'): #Rotate Angle
rotateAngle=self.ieeeDoubleFromIbmData(record[2:10])
thisSref.rotateAngle=rotateAngle
thisSref.rotateAngle=rotateAngle
if(self.debugToTerminal==1):
print("\t\t\tRotate Angle (CCW):"+str(rotateAngle))
elif(idBits==b'\x10\x03'): #XY Data Points
@ -328,11 +333,11 @@ class Gds2reader:
print("\t\t\tEndSref")
break;
return thisSref
def readAref(self): #an array of references
if(self.debugToTerminal==1):
print("\t\t\tBeginAref")
thisAref = GdsAref()
while 1:
record = self.readNextRecord()
@ -369,7 +374,7 @@ class Gds2reader:
print("\t\t\tMagnification:"+str(magFactor))
elif(idBits==b'\x1C\x05'): #Rotate Angle
rotateAngle=self.ieeeDoubleFromIbmData(record[2:10])
thisAref.rotateAngle=rotateAngle
thisAref.rotateAngle=rotateAngle
if(self.debugToTerminal==1):
print("\t\t\tRotate Angle (CCW):"+str(rotateAngle))
elif(idBits==b'\x10\x03'): #XY Data Points
@ -388,11 +393,11 @@ class Gds2reader:
print("\t\t\tEndAref")
break;
return thisAref
def readText(self):
if(self.debugToTerminal==1):
print("\t\t\tBeginText")
thisText=GdsText()
while 1:
record = self.readNextRecord()
@ -414,9 +419,9 @@ class Gds2reader:
self.layoutObject.layerNumbersInUse += [drawingLayer]
if(self.debugToTerminal==1):
print("\t\tDrawing Layer: "+str(drawingLayer))
elif(idBits==b'\x16\x02'): #Purpose
elif(idBits==b'\x16\x02'): #Purpose TEXTTYPE
purposeLayer = struct.unpack(">h",record[2:4])[0]
thisText.purposeLayer=purposeLayer
thisText.purposeLayer=purposeLayer
if(self.debugToTerminal==1):
print("\t\tPurpose Layer: "+str(purposeLayer))
elif(idBits==b'\x1A\x01'): #Transformation
@ -492,11 +497,11 @@ class Gds2reader:
print("\t\t\tEndText")
break;
return thisText
def readNode(self):
if(self.debugToTerminal==1):
print("\t\t\tBeginNode")
##reads in a node type structure = an electrical net
thisNode = GdsNode()
while 1:
@ -538,11 +543,11 @@ class Gds2reader:
print("\t\t\tEndNode")
break;
return thisNode
def readBox(self):
if(self.debugToTerminal==1):
print("\t\t\tBeginBox")
##reads in a gds BOX structure
thisBox = GdsBox()
while 1:
@ -565,9 +570,9 @@ class Gds2reader:
self.layoutObject.layerNumbersInUse += [drawingLayer]
if(self.debugToTerminal==1):
print("\t\tDrawing Layer: "+str(drawingLayer))
elif(idBits==b'\x16\x02'): #Purpose
elif(idBits==b'\x16\x02'): #Purpose TEXTYPE
purposeLayer = struct.unpack(">h",record[2:4])[0]
thisBox.purposeLayer=purposeLayer
thisBox.purposeLayer=purposeLayer
if(self.debugToTerminal==1):
print("\t\tPurpose Layer: "+str(purposeLayer))
elif(idBits==b'\x2D\x00'): #Box
@ -589,14 +594,14 @@ class Gds2reader:
print("\t\t\tEndBox")
break;
return thisBox
def readNextStructure(self):
thisStructure = GdsStructure()
thisStructure = GdsStructure()
record = self.readNextRecord()
idBits = record[0:2]
# Begin structure
if(idBits==b'\x05\x02' and len(record)==26):
createYear = struct.unpack(">h",record[2:4])[0]
createYear = struct.unpack(">h",record[2:4])[0]
createMonth = struct.unpack(">h",record[4:6])[0]
createDay = struct.unpack(">h",record[6:8])[0]
createHour = struct.unpack(">h",record[8:10])[0]
@ -623,7 +628,7 @@ class Gds2reader:
idBits = record[0:2]
if idBits==b'\x07\x00': break; #we've reached the end of the structure
elif(idBits==b'\x06\x06'):
structName = self.stripNonASCII(record[2::])
structName = self.stripNonASCII(record[2::])
thisStructure.name = structName
if(self.debugToTerminal==1):
print("\tStructure Name: "+structName)
@ -641,11 +646,11 @@ class Gds2reader:
thisStructure.nodes+=[self.readNode()]
elif(idBits==b'\x2E\x02'):
thisStructure.boxes+=[self.readBox()]
if(self.debugToTerminal==1):
if(self.debugToTerminal==1):
print("\tEnd of Structure.")
self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object
return 1
def readGds2(self):
if(self.readHeader()): #did the header read ok?
record = self.readNextStructure()
@ -662,7 +667,7 @@ class Gds2reader:
print("There was an error reading the structure list.")
else:
print("There was an error parsing the GDS header. Aborting...")
def loadFromFile(self, fileName):
self.fileHandle = open(fileName,"rb")
self.readGds2()
@ -690,11 +695,11 @@ class Gds2reader:
def findStruct_readNextStruct(self,findStructName):
self.debugToTerminal=0
thisStructure = GdsStructure()
thisStructure = GdsStructure()
record = self.readNextRecord()
idBits = record[0:2]
if(idBits==('\x05','\x02') and len(record)==26):
createYear = struct.unpack(">h",record[2]+record[3])[0]
createYear = struct.unpack(">h",record[2]+record[3])[0]
createMonth = struct.unpack(">h",record[4]+record[5])[0]
createDay = struct.unpack(">h",record[6]+record[7])[0]
createHour = struct.unpack(">h",record[8]+record[9])[0]
@ -738,7 +743,7 @@ class Gds2reader:
thisStructure.nodes+=[self.readNode()]
elif(idBits==('\x2E','\x02')):
thisStructure.boxes+=[self.readBox()]
if(self.debugToTerminal==1):
if(self.debugToTerminal==1):
print("\tEnd of Structure.")
self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object
if(wantedStruct == 0):
@ -766,11 +771,11 @@ class Gds2reader:
def findLabel_readNextStruct(self,findLabelName):
self.debugToTerminal=0
thisStructure = GdsStructure()
thisStructure = GdsStructure()
record = self.readNextRecord()
idBits = record[0:2]
if(idBits==('\x05','\x02') and len(record)==26):
createYear = struct.unpack(">h",record[2]+record[3])[0]
createYear = struct.unpack(">h",record[2]+record[3])[0]
createMonth = struct.unpack(">h",record[4]+record[5])[0]
createDay = struct.unpack(">h",record[6]+record[7])[0]
createHour = struct.unpack(">h",record[8]+record[9])[0]
@ -820,7 +825,7 @@ class Gds2reader:
thisStructure.nodes+=[self.readNode()]
elif(idBits==('\x2E','\x02')):
thisStructure.boxes+=[self.readBox()]
if(self.debugToTerminal==1):
if(self.debugToTerminal==1):
print("\tEnd of Structure.")
self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object
if(wantedLabel == 0):
@ -828,4 +833,3 @@ class Gds2reader:
else:
#print("\tDone with collectting bound. Return")
return [0,wantedtexts]

View File

@ -5,37 +5,37 @@ from .gdsPrimitives import *
class Gds2writer:
"""Class to take a populated layout class and write it to a file in GDSII format"""
## Based on info from http://www.rulabinsky.com/cavd/text/chapc.html
def __init__(self,layoutObject):
self.fileHandle = 0
self.layoutObject = layoutObject
self.debugToTerminal=0 #do we dump debug data to the screen
def print64AsBinary(self,number):
#debugging method for binary inspection
for index in range(0,64):
print((number>>(63-index))&0x1,eol='')
print("\n")
def ieeeDoubleFromIbmData(self,ibmData):
#the GDS double is in IBM 370 format like this:
#(1)sign (7)exponent (56)mantissa
#exponent is excess 64, mantissa has no implied 1
#a normal IEEE double is like this:
#(1)sign (11)exponent (52)mantissa
#(1)sign (11)exponent (52)mantissa
data = struct.unpack('>q',ibmData)[0]
sign = (data >> 63)&0x01
exponent = (data >> 56) & 0x7f
mantissa = data<<8 #chop off sign and exponent
if mantissa == 0:
newFloat = 0.0
else:
exponent = ((exponent-64)*4)+1023 #convert to double exponent
#re normalize
while mantissa & 0x8000000000000000 == 0:
while mantissa & 0x8000000000000000 == 0:
mantissa<<=1
exponent-=1
exponent-=1
mantissa<<=1 #remove the assumed high bit
exponent-=1
#check for underflow error -- should handle these properly!
@ -49,12 +49,12 @@ class Gds2writer:
#convert back to double
newFloat = struct.unpack('>d',asciiDouble)[0]
return newFloat
def ibmDataFromIeeeDouble(self,ieeeDouble):
asciiDouble = struct.pack('>d',ieeeDouble)
data = struct.unpack('>q',asciiDouble)[0]
sign = (data >> 63) & 0x01
exponent = ((data >> 52) & 0x7ff)-1023
exponent = ((data >> 52) & 0x7ff)-1023
mantissa = data << 12 #chop off sign and exponent
if(ieeeDouble == 0):
mantissa = 0
@ -70,14 +70,14 @@ class Gds2writer:
for index in range (0,-exponent&3):
mantissa >>= 1
mantissa = mantissa & 0x7fffffffffffffff
exponent = (exponent+3) >> 2
exponent = (exponent+3) >> 2
exponent+=64
newFloat =(sign<<63)|(exponent<<56)|((mantissa>>8)&0xffffffffffffff)
asciiDouble = struct.pack('>q',newFloat)
newFloat =(sign<<63)|(exponent<<56)|((mantissa>>8)&0xffffffffffffff)
asciiDouble = struct.pack('>q',newFloat)
return asciiDouble
def ieeeFloatCheck(self,aFloat):
#debugging method for float construction
asciiDouble = struct.pack('>d',aFloat)
@ -90,7 +90,7 @@ class Gds2writer:
asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12))
newFloat = struct.unpack('>d',asciiDouble)[0]
print("Check:"+str(newFloat))
def writeRecord(self,record):
recordLength = len(record)+2 #make sure to include this in the length
recordLengthAscii=struct.pack(">h",recordLength)
@ -127,7 +127,7 @@ class Gds2writer:
libraryName = self.layoutObject.info["libraryName"].encode() + "\0"
else:
libraryName = self.layoutObject.info["libraryName"].encode()
self.writeRecord(idBits+libraryName)
self.writeRecord(idBits+libraryName)
## reference libraries
if("referenceLibraries" in self.layoutObject.info):
idBits=b'\x1F\x06'
@ -158,11 +158,11 @@ class Gds2writer:
mask = self.layoutObject.info["mask"]
self.writeRecord(idBits+mask)
if("units" in self.layoutObject.info):
idBits=b'\x03\x05'
idBits=b'\x03\x05'
userUnits=self.ibmDataFromIeeeDouble(self.layoutObject.info["units"][0])
dbUnits=self.ibmDataFromIeeeDouble((self.layoutObject.info["units"][0]*1e-6/self.layoutObject.info["units"][1])*self.layoutObject.info["units"][1])
#User Units are hardcoded, since the floating point implementation of gdsMill is not adequate,
#User Units are hardcoded, since the floating point implementation of gdsMill is not adequate,
#resulting in a different value being written in output stream. Hardcoded to sram compiler's outputed gds units.
#db="39225c17d04dad2a"
#uu="3e20c49ba5e353f8"
@ -172,17 +172,17 @@ class Gds2writer:
#dbUnits="39225c17d04dad2a".decode("hex")
#db=39225c17d04dad2a
self.writeRecord(idBits+userUnits+dbUnits)
if(self.debugToTerminal==1):
if(self.debugToTerminal==1):
print("writer: userUnits %s"%(userUnits.encode("hex")))
print("writer: dbUnits %s"%(dbUnits.encode("hex")))
#self.ieeeFloatCheck(1.3e-6)
print("End of GDSII Header Written")
return 1
def writeBoundary(self,thisBoundary):
idBits=b'\x08\x00' #record Type
self.writeRecord(idBits)
@ -216,7 +216,7 @@ class Gds2writer:
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writePath(self,thisPath): #writes out a path structure
idBits=b'\x09\x00' #record Type
self.writeRecord(idBits)
@ -236,6 +236,10 @@ class Gds2writer:
idBits=b'\x16\x02' #purpose layer
purposeLayer = struct.pack(">h",thisPath.purposeLayer)
self.writeRecord(idBits+purposeLayer)
if(thisPath.dataType is not None):
idBits=b'\x0E\x02' #Data type
dataType = struct.pack(">h",thisPath.dataType)
self.writeRecord(idBits+dataType)
if(thisPath.pathType):
idBits=b'\x21\x02' #Path type
pathType = struct.pack(">h",thisPath.pathType)
@ -256,7 +260,7 @@ class Gds2writer:
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writeSref(self,thisSref): #reads in a reference to another structure
idBits=b'\x0A\x00' #record Type
self.writeRecord(idBits)
@ -292,7 +296,7 @@ class Gds2writer:
magFactor=self.ibmDataFromIeeeDouble(thisSref.magFactor)
self.writeRecord(idBits+magFactor)
if(thisSref.rotateAngle!=""):
idBits=b'\x1C\x05'
idBits=b'\x1C\x05'
rotateAngle=self.ibmDataFromIeeeDouble(thisSref.rotateAngle)
self.writeRecord(idBits+rotateAngle)
if(thisSref.coordinates!=""):
@ -308,7 +312,7 @@ class Gds2writer:
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writeAref(self,thisAref): #an array of references
idBits=b'\x0B\x00' #record Type
self.writeRecord(idBits)
@ -344,7 +348,7 @@ class Gds2writer:
magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor)
self.writeRecord(idBits+magFactor)
if(thisAref.rotateAngle!=""):
idBits=b'\x1C\x05'
idBits=b'\x1C\x05'
rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle)
self.writeRecord(idBits+rotateAngle)
if(thisAref.coordinates):
@ -359,7 +363,7 @@ class Gds2writer:
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writeText(self,thisText):
idBits=b'\x0C\x00' #record Type
self.writeRecord(idBits)
@ -375,9 +379,8 @@ class Gds2writer:
idBits=b'\x0D\x02' #drawing layer
drawingLayer = struct.pack(">h",thisText.drawingLayer)
self.writeRecord(idBits+drawingLayer)
# TextType is always a 0 per GDS specification
idBits=b'\x16\x02' #purpose layer
purposeLayer = struct.pack(">h",0)
idBits=b'\x16\x02' #purpose layer TEXTTYPE
purposeLayer = struct.pack(">h",thisText.purposeLayer)
self.writeRecord(idBits+purposeLayer)
if(thisText.transFlags != ""):
idBits=b'\x1A\x01'
@ -396,7 +399,7 @@ class Gds2writer:
magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor)
self.writeRecord(idBits+magFactor)
if(thisText.rotateAngle!=""):
idBits=b'\x1C\x05'
idBits=b'\x1C\x05'
rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle)
self.writeRecord(idBits+rotateAngle)
if(thisText.pathType !=""):
@ -408,12 +411,12 @@ class Gds2writer:
pathWidth = struct.pack(">i",thisText.pathWidth)
self.writeRecord(idBits+pathWidth)
if(thisText.presentationFlags!=""):
idBits=b'\x1A\x01'
idBits=b'\x1A\x01'
font = thisText.presentationFlags[0]<<4
verticalFlags = int(thisText.presentationFlags[1])<<2
horizontalFlags = int(thisText.presentationFlags[2])
presentationFlags = struct.pack(">H",font|verticalFlags|horizontalFlags)
self.writeRecord(idBits+transFlags)
self.writeRecord(idBits+transFlags)
if(thisText.coordinates!=""):
idBits=b'\x10\x03' #XY Data Points
coordinateRecord = idBits
@ -427,11 +430,11 @@ class Gds2writer:
idBits=b'\x19\x06'
textString = thisText.textString
self.writeRecord(idBits+textString.encode())
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writeNode(self,thisNode):
idBits=b'\x15\x00' #record Type
self.writeRecord(idBits)
@ -446,11 +449,11 @@ class Gds2writer:
if(thisNode.drawingLayer!=""):
idBits=b'\x0D\x02' #drawig layer
drawingLayer = struct.pack(">h",thisNode.drawingLayer)
self.writeRecord(idBits+drawingLayer)
self.writeRecord(idBits+drawingLayer)
if(thisNode.nodeType!=""):
idBits=b'\x2A\x02'
nodeType = struct.pack(">h",thisNode.nodeType)
self.writeRecord(idBits+nodeType)
self.writeRecord(idBits+nodeType)
if(thisText.coordinates!=""):
idBits=b'\x10\x03' #XY Data Points
coordinateRecord = idBits
@ -460,11 +463,11 @@ class Gds2writer:
coordinateRecord+=x
coordinateRecord+=y
self.writeRecord(coordinateRecord)
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writeBox(self,thisBox):
idBits=b'\x2E\x02' #record Type
self.writeRecord(idBits)
@ -487,7 +490,7 @@ class Gds2writer:
if(thisBox.boxValue!=""):
idBits=b'\x2D\x00'
boxValue = struct.pack(">h",thisBox.boxValue)
self.writeRecord(idBits+boxValue)
self.writeRecord(idBits+boxValue)
if(thisBox.coordinates!=""):
idBits=b'\x10\x03' #XY Data Points
coordinateRecord = idBits
@ -497,11 +500,11 @@ class Gds2writer:
coordinateRecord+=x
coordinateRecord+=y
self.writeRecord(coordinateRecord)
idBits=b'\x11\x00' #End Of Element
coordinateRecord = idBits
self.writeRecord(coordinateRecord)
def writeNextStructure(self,structureName):
#first put in the structure head
thisStructure = self.layoutObject.structures[structureName]
@ -528,7 +531,7 @@ class Gds2writer:
structureName = structureName + '\x00'
self.writeRecord(idBits+structureName.encode())
#now go through all the structure elements and write them in
for boundary in thisStructure.boundaries:
self.writeBoundary(boundary)
for path in thisStructure.paths:
@ -546,7 +549,7 @@ class Gds2writer:
#put in the structure tail
idBits=b'\x07\x00'
self.writeRecord(idBits)
def writeGds2(self):
self.writeHeader(); #first, put the header in
#go through each structure in the layout and write it to the file
@ -555,7 +558,7 @@ class Gds2writer:
#at the end, put in the END LIB record
idBits=b'\x04\x00'
self.writeRecord(idBits)
def writeToFile(self,fileName):
self.fileHandle = open(fileName,"wb")
self.writeGds2()

View File

@ -1,5 +1,12 @@
import math
from globals import OPTS
# default purpose layer is used for addText() in vlsiLayout.py
if OPTS.tech_name == "s8":
purposeLayer=20
else:
purposeLayer=0
class GdsStructure:
"""Class represent a GDS Structure Object"""
def __init__(self):
@ -9,7 +16,7 @@ class GdsStructure:
#these are the primitives defined in GDS2, and we will maintain lists of them all
self.boundaries=[]
self.paths=[]
self.srefs=[]
self.srefs=[]
self.arefs=[]
self.texts=[]
self.nodes=[]
@ -23,7 +30,7 @@ class GdsBoundary:
self.drawingLayer=""
self.purposeLayer=0
self.coordinates=""
class GdsPath:
"""Class represent a GDS Path Object"""
def __init__(self):
@ -32,9 +39,10 @@ class GdsPath:
self.drawingLayer=""
self.purposeLayer=0
self.pathType=""
self.dataType=None
self.pathWidth=""
self.coordinates=""
def equivalentBoundaryCoordinates(self):
"""Convert the path to a set of boundary coordinates that define it"""
halfWidth = (self.pathWidth/2)
@ -61,7 +69,7 @@ class GdsPath:
nextX = None;
nextY = None;
if lastX==None: #start of the path
if nextX>x:#moving right
if nextX>x:#moving right
boundaryEquivalent+=[(x,y+halfWidth)]
if nextX<x:#moving left
boundaryEquivalent+=[(x,y-halfWidth)]
@ -95,9 +103,9 @@ class GdsPath:
boundaryEquivalent+=[(x-halfWidth,y+halfWidth)]
if(y < lastY and x > nextX):
boundaryEquivalent+=[(x+halfWidth,y-halfWidth)]
if nextX == None: #end of path, put in the last 2 points
if lastX<x:#moving right
if nextX == None: #end of path, put in the last 2 points
if lastX<x:#moving right
boundaryEquivalent+=[(x,y+halfWidth)]
if lastX>x:#moving left
boundaryEquivalent+=[(x,y-halfWidth)]
@ -139,7 +147,7 @@ class GdsText:
self.elementFlags=""
self.plex=""
self.drawingLayer=""
self.purposeLayer=0
self.purposeLayer=purposeLayer
self.transFlags=[0,0,0]
self.magFactor=""
self.rotateAngle=""
@ -148,7 +156,7 @@ class GdsText:
self.presentationFlags=""
self.coordinates=""
self.textString = ""
class GdsNode:
"""Class represent a GDS Node Object"""
def __init__(self):
@ -157,7 +165,7 @@ class GdsNode:
self.drawingLayer=""
self.nodeType=""
self.coordinates=""
class GdsBox:
"""Class represent a GDS Box Object"""
def __init__(self):

View File

@ -304,6 +304,7 @@ def read_config(config_file, is_unit_test=True):
except:
debug.error("Unable to read configuration file: {0}".format(config_file),2)
OPTS.overridden = {}
for k, v in config.__dict__.items():
# The command line will over-ride the config file
# except in the case of the tech name! This is because the tech name
@ -311,6 +312,7 @@ def read_config(config_file, is_unit_test=True):
# Note that if we re-read a config file, nothing will get read again!
if k not in OPTS.__dict__ or k == "tech_name":
OPTS.__dict__[k] = v
OPTS.overridden[k] = True
# Massage the output path to be an absolute one
if not OPTS.output_path.endswith('/'):
@ -504,6 +506,11 @@ def import_tech():
except ImportError:
debug.error("Could not load tech module.", -1)
# Add custom modules of the technology to the path, if they exist
custom_mod_path = os.path.join(tech_path, "modules/")
if os.path.exists(custom_mod_path):
sys.path.append(custom_mod_path)
def print_time(name, now_time, last_time=None, indentation=2):
""" Print a statement about the time delta. """

View File

@ -5,19 +5,15 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys
from tech import drc, parameter
import debug
import design
import math
from math import log,sqrt,ceil
import contact
import pgates
from sram_factory import factory
from math import log
from tech import drc
from vector import vector
from globals import OPTS
class bank(design.design):
"""
Dynamically generated a single bank including bitcell array,
@ -57,6 +53,7 @@ class bank(design.design):
def create_netlist(self):
self.compute_sizes()
self.add_modules()
self.add_pins() # Must create the replica bitcell array first
@ -334,7 +331,7 @@ class bank(design.design):
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
@ -500,6 +497,8 @@ class bank(design.design):
Create a 2:4 or 3:8 column address decoder.
"""
# Height is a multiple of DFF so that it can be staggered
# and rows do not align with the control logic module
self.dff = factory.create(module_type="dff")
if self.col_addr_size == 0:
@ -606,12 +605,12 @@ class bank(design.design):
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
name = self.control_signals[port][signal]
bus_pos = vector(self.bus_xoffset[port][name].x, out_pos.y)
self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_path("m3",[out_pos, bus_pos])
self.add_via_center(layers=self.m2_stack,
offset=bus_pos)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=out_pos)
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=out_pos)
@ -649,7 +648,7 @@ class bank(design.design):
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2*self.m1_pitch
self.bus_xoffset[0] = self.create_bus(layer="metal2",
self.bus_xoffset[0] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals[0],
@ -664,7 +663,7 @@ class bank(design.design):
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
self.max_y_offset - control_bus_length)
# The bus for the right port is reversed so that the rbl_wl is closest to the array
self.bus_xoffset[1] = self.create_bus(layer="metal2",
self.bus_xoffset[1] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=list(reversed(self.control_signals[1])),
@ -681,9 +680,13 @@ class bank(design.design):
inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}"
inst2_bl_name = inst2.mod.get_bl_names()+"_{}"
inst2_br_name = inst2.mod.get_br_names()+"_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name,
inst2_bl_name=inst2_bl_name, inst2_br_name=inst2_br_name)
# Connect the replica bitlines
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port])
@ -757,7 +760,7 @@ class bank(design.design):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset)
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name):
"""
@ -788,8 +791,8 @@ class bank(design.design):
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
inst2_bl_name="bl_{}", inst2_br_name="br_{}"):
inst1_bl_name, inst1_br_name,
inst2_bl_name, inst2_br_name):
"""
Connect the bl and br of two modules.
"""
@ -818,7 +821,7 @@ class bank(design.design):
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc()
mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].rx() + 0.5*self.bitcell_array_inst.lx(),0)
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_port_address_right(self, port):
@ -830,7 +833,7 @@ class bank(design.design):
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).rc()
mid1 = driver_wl_pos.scale(0,1) + vector(0.5*self.port_address_inst[port].lx() + 0.5*self.bitcell_array_inst.rx(),0)
mid2 = mid1.scale(1,0)+bitcell_wl_pos.scale(0,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """
@ -866,8 +869,7 @@ class bank(design.design):
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map, offset)
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
@ -879,7 +881,7 @@ class bank(design.design):
wl_name = "wl_{}".format(i)
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
self.add_label(text=wl_name,
layer="metal1",
layer="m1",
offset=wl_pin.center())
# Add the bitline names
@ -889,10 +891,10 @@ class bank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
br_pin = self.bitcell_array_inst.get_pin(br_name)
self.add_label(text=bl_name,
layer="metal2",
layer="m2",
offset=bl_pin.center())
self.add_label(text=br_name,
layer="metal2",
layer="m2",
offset=br_pin.center())
# # Add the data output names to the sense amp output
@ -900,7 +902,7 @@ class bank(design.design):
# data_name = "data_{}".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out_{}".format(i),
# layer="metal2",
# layer="m2",
# offset=data_pin.center())
# Add labels on the decoder
@ -910,10 +912,9 @@ class bank(design.design):
pin_name = "in_{}".format(i)
data_pin = self.wordline_driver_inst[port].get_pin(pin_name)
self.add_label(text=data_name,
layer="metal1",
layer="m1",
offset=data_pin.center())
def route_control_lines(self, port):
""" Route the control lines of the entire bank """
@ -921,29 +922,31 @@ class bank(design.design):
# From control signal to the module pin
# Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3
write_inst = 0
read_inst = 0
connection = []
connection.append((self.prefix+"p_en_bar{}".format(port), self.port_data_inst[port].get_pin("p_en_bar").lc()))
connection.append((self.prefix + "p_en_bar{}".format(port),
self.port_data_inst[port].get_pin("p_en_bar").lc()))
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
connection.append((self.prefix + "wl_en{}".format(port),
self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
if port in self.write_ports:
if port % 2:
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").rc()))
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en").rc()))
else:
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc()))
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en").lc()))
if port in self.read_ports:
connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc()))
connection.append((self.prefix + "s_en{}".format(port),
self.port_data_inst[port].get_pin("s_en").lc()))
for (control_signal, pin_pos) in connection:
control_mid_pos = self.bus_xoffset[port][control_signal]
control_pos = vector(self.bus_xoffset[port][control_signal].x ,pin_pos.y)
self.add_wire(("metal1","via1","metal2"), [control_mid_pos, control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=control_pos)
@ -957,11 +960,11 @@ class bank(design.design):
mid_pos = pin_pos - vector(0,2*self.m2_gap) # to route down to the top of the bus
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])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos])
self.add_via_center(layers=self.m1_stack,
offset=control_pos)
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
#Decoder is assumed to have settled before the negative edge of the clock. Delay model relies on this assumption
stage_effort_list = []

View File

@ -42,6 +42,10 @@ class bank_select(design.design):
self.place_instances()
self.route_instances()
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
self.width = max([x.rx() for x in self.inv_inst])
self.add_boundary()
self.DRC_LVS()
@ -96,14 +100,11 @@ class bank_select(design.design):
self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width)
self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
# Include the M1 pitches for the supply rails and spacing
self.height = self.yoffset_maxpoint + 2*self.m1_pitch
self.width = self.xoffset_inv + self.inv4x.width
def create_instances(self):
@ -197,7 +198,7 @@ class bank_select(design.design):
mirror=mirror)
# They all get inverters on the output
inv_inst.place(offset=[self.xoffset_inv, y_offset],
inv_inst.place(offset=[logic_inst.rx(), y_offset],
mirror=mirror)
@ -208,18 +209,18 @@ class bank_select(design.design):
xoffset_bank_sel = bank_sel_inv_pin.lx()
bank_sel_line_pos = vector(xoffset_bank_sel, 0)
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
self.add_path("metal2", [bank_sel_line_pos, bank_sel_line_end])
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
self.add_via_center(layers=self.m1_stack,
offset=bank_sel_inv_pin.lc())
# Route the pin to the left edge as well
bank_sel_pin_pos=vector(0, 0)
bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y)
self.add_layout_pin_segment_center(text="bank_sel",
layer="metal3",
layer="m3",
start=bank_sel_pin_pos,
end=bank_sel_pin_end)
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=bank_sel_pin_end,
directions=("H","H"))
@ -227,10 +228,10 @@ class bank_select(design.design):
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
self.add_label_pin(text="bank_sel_bar",
layer="metal2",
layer="m2",
offset=vector(xoffset_bank_sel_bar, 0),
height=self.inv4x.height)
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=bank_sel_bar_pin.rc())
@ -251,14 +252,14 @@ class bank_select(design.design):
out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0)
in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0)
post = inv_inst.get_pin("A").rc()
self.add_path("metal1", [pre, out_position, in_position, post])
self.add_path("m1", [pre, out_position, in_position, post])
# Connect the logic B input to bank_sel/bank_sel_bar
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1m2.height,0)
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0)
input_pos = vector(xoffset_bank_signal, logic_pos.y)
self.add_path("metal2",[logic_pos, input_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_path("m2",[logic_pos, input_pos])
self.add_via_center(layers=self.m1_stack,
offset=logic_pos,
directions=("H","H"))
@ -266,14 +267,14 @@ class bank_select(design.design):
# Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").lc()
input_pos = vector(0,logic_pos.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=logic_pos,
directions=("H","H"))
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=logic_pos,
directions=("H","H"))
self.add_layout_pin_segment_center(text=input_name,
layer="metal3",
layer="m3",
start=input_pos,
end=logic_pos)
@ -294,34 +295,34 @@ class bank_select(design.design):
for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n)
supply_offset = supply_pin.ll().scale(0,1)
self.add_rect(layer="metal1",
self.add_rect(layer="m1",
offset=supply_offset,
width=self.width)
# Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=pin_pos,
directions=("H","H"))
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=pin_pos,
directions=("H","H"))
self.add_layout_pin_rect_center(text=n,
layer="metal3",
layer="m3",
offset=pin_pos)
# Add vdd/gnd supply rails
gnd_pin = inv_inst.get_pin("gnd")
gnd_pin = self.inv_inst[num].get_pin("gnd")
left_gnd_pos = vector(0, gnd_pin.cy())
self.add_layout_pin_segment_center(text="gnd",
layer="metal1",
layer="m1",
start=left_gnd_pos,
end=gnd_pin.rc())
vdd_pin = inv_inst.get_pin("vdd")
vdd_pin = self.inv_inst[num].get_pin("vdd")
left_vdd_pos = vector(0, vdd_pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="metal1",
layer="m1",
start=left_vdd_pos,
end=vdd_pin.rc())

View File

@ -5,27 +5,20 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
from bitcell_base_array import bitcell_base_array
from tech import drc, spice
from vector import vector
from globals import OPTS
from sram_factory import factory
import logical_effort
class bitcell_array(design.design):
class bitcell_array(bitcell_base_array):
"""
Creates a rows x cols array of memory cells. Assumes bit-lines
and word line is connected by abutment.
Connects the word lines and bit lines.
"""
def __init__(self, cols, rows, name):
design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.column_size = cols
self.row_size = rows
def __init__(self, cols, rows, name, column_offset=0):
super().__init__(cols, rows, name, column_offset)
self.create_netlist()
if not OPTS.netlist_only:
@ -33,7 +26,7 @@ class bitcell_array(design.design):
# We don't offset this because we need to align
# the replica bitcell in the control logic
#self.offset_all_coordinates()
# self.offset_all_coordinates()
def create_netlist(self):
@ -44,27 +37,7 @@ class bitcell_array(design.design):
def create_layout(self):
# We increase it by a well enclosure so the precharges don't overlap our wells
self.height = self.row_size*self.cell.height
self.width = self.column_size*self.cell.width
xoffset = 0.0
for col in range(self.column_size):
yoffset = 0.0
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
if row % 2:
tempy = yoffset + self.cell.height
dir_key = "MX"
else:
tempy = yoffset
dir_key = ""
self.cell_inst[row,col].place(offset=[xoffset, tempy],
mirror=dir_key)
yoffset += self.cell.height
xoffset += self.cell.width
self.place_array("bit_r{0}_c{1}")
self.add_layout_pins()
@ -72,41 +45,11 @@ class bitcell_array(design.design):
self.DRC_LVS()
def add_pins(self):
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
self.add_pin(cell_column+"_{0}".format(col), "INOUT")
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add the modules used in this design """
self.cell = factory.create(module_type="bitcell")
self.add_mod(self.cell)
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
pin_names = self.cell.get_all_bitline_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(col))
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(row))
bitcell_pins.append("vdd")
bitcell_pins.append("gnd")
return bitcell_pins
def create_instances(self):
""" Create the module instances used in this design """
self.cell_inst = {}
@ -117,38 +60,6 @@ class bitcell_array(design.design):
mod=self.cell)
self.connect_inst(self.get_bitcell_pins(col, row))
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
bl_pin = self.cell_inst[0,col].get_pin(cell_column)
self.add_layout_pin(text=cell_column+"_{0}".format(col),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1,0),
width=bl_pin.width(),
height=self.height)
for row in range(self.row_size):
for cell_row in row_list:
wl_pin = self.cell_inst[row,0].get_pin(cell_row)
self.add_layout_pin(text=cell_row+"_{0}".format(row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0,1),
width=self.width,
height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW."""
from tech import drc, parameter
@ -160,10 +71,10 @@ class bitcell_array(design.design):
freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
#Calculate the bitcell power which currently only includes leakage
# Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load)
#Leakage power grows with entire array and bitlines.
# Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size)
return total_power
@ -173,7 +84,7 @@ class bitcell_array(design.design):
width = 0
else:
width = self.width
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_metal1"))
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
return wl_wire
@ -183,7 +94,7 @@ class bitcell_array(design.design):
else:
height = self.height
bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_metal1"))
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1"))
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire

View File

@ -0,0 +1,157 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
from tech import cell_properties
class bitcell_base_array(design.design):
"""
Abstract base class for bitcell-arrays -- bitcell, dummy
"""
def __init__(self, cols, rows, name, column_offset):
design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.column_size = cols
self.row_size = rows
self.column_offset = column_offset
def get_all_bitline_names(self):
res = list()
bitline_names = self.cell.get_all_bitline_names()
# We have to keep the order of self.pins, otherwise we connect
# it wrong in the spice netlist
for pin in self.pins:
for bl_name in bitline_names:
if bl_name in pin:
res.append(pin)
return res
def get_all_wordline_names(self):
res = list()
wordline_names = self.cell.get_all_wl_names()
# We have to keep the order of self.pins, otherwise we connect
# it wrong in the spice netlist
for pin in self.pins:
for wl_name in wordline_names:
if wl_name in pin:
res.append(pin)
return res
def add_pins(self):
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
self.add_pin(cell_column+"_{0}".format(col), "INOUT")
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
pin_names = self.cell.get_all_bitline_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(col))
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(row))
bitcell_pins.append("vdd")
bitcell_pins.append("gnd")
return bitcell_pins
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
bl_pin = self.cell_inst[0,col].get_pin(cell_column)
self.add_layout_pin(text=cell_column+"_{0}".format(col),
layer=bl_pin.layer,
offset=bl_pin.ll().scale(1,0),
width=bl_pin.width(),
height=self.height)
for row in range(self.row_size):
for cell_row in row_list:
wl_pin = self.cell_inst[row,0].get_pin(cell_row)
self.add_layout_pin(text=cell_row+"_{0}".format(row),
layer=wl_pin.layer,
offset=wl_pin.ll().scale(0,1),
width=self.width,
height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset
dir_y = False
# If we mirror the current cell on the y axis adjust the x position
if cell_properties.bitcell.mirror.y and (col + col_offset) % 2:
tempx = xoffset + self.cell.width
dir_y = True
return (tempx, dir_y)
def _adjust_y_offset(self, yoffset, row, row_offset):
tempy = yoffset
dir_x = False
# If we mirror the current cell on the x axis adjust the y position
if cell_properties.bitcell.mirror.x and (row + row_offset) % 2:
tempy = yoffset + self.cell.height
dir_x = True
return (tempy, dir_x)
def place_array(self, name_template, row_offset=0):
# We increase it by a well enclosure so the precharges don't overlap our wells
self.height = self.row_size*self.cell.height
self.width = self.column_size*self.cell.width
xoffset = 0.0
for col in range(self.column_size):
yoffset = 0.0
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
for row in range(self.row_size):
name = name_template.format(row, col)
tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset)
if dir_x and dir_y:
dir_key = "XY"
elif dir_x:
dir_key = "MX"
elif dir_y:
dir_key = "MY"
else:
dir_key = ""
self.cell_inst[row,col].place(offset=[tempx, tempy],
mirror=dir_key)
yoffset += self.cell.height
xoffset += self.cell.width

View File

@ -8,6 +8,7 @@
from math import log
import design
from tech import drc, parameter
from tech import cell_properties as props
import debug
import contact
from sram_factory import factory
@ -371,7 +372,7 @@ class control_logic(design.design):
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)
self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height)
def create_instances(self):
@ -483,8 +484,8 @@ class control_logic(design.design):
vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by()
in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos)
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1, in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_wire(self.m1_stack,[out_pos, mid1, in_pos])
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
@ -509,10 +510,10 @@ class control_logic(design.design):
clk_pin = self.clk_buf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
layer="m2",
start=clk_pos,
end=clk_pos.scale(1,0))
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=clk_pos)
@ -521,9 +522,9 @@ class control_logic(design.design):
mid1 = vector(out_pos.x,2*self.m2_pitch)
mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
bus_pos = self.rail_offsets["clk_buf"]
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, mid2, bus_pos])
self.add_wire(("m3","via2","m2"),[out_pos, mid1, mid2, bus_pos])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=self.clk_buf_inst.get_pin("Z").center())
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
@ -552,21 +553,21 @@ class control_logic(design.design):
out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_path("metal1",[out_pos, mid1, in_pos])
self.add_path("m1",[out_pos, mid1, in_pos])
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("A").center())
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("Z").center())
def create_gated_clk_buf_row(self):
@ -587,9 +588,9 @@ class control_logic(design.design):
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("m3", "via2", "m2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_buf_inst.get_pin("Z").center())
def create_wlen_row(self):
@ -637,7 +638,7 @@ class control_logic(design.design):
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
self.add_wire(self.m1_stack,[out_pos, mid1,in_pos])
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
@ -646,7 +647,7 @@ class control_logic(design.design):
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs_bar"
input_name = "cs"
# GATE FOR S_EN
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
mod=self.sen_and3)
@ -669,7 +670,7 @@ class control_logic(design.design):
if self.port_type=="rw":
input_name = "we_bar"
else:
input_name = "cs_bar"
input_name = "cs"
sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name])
self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets)
@ -694,9 +695,9 @@ class control_logic(design.design):
# Connect to rail
rbl_map = zip(["Z"], ["rbl_bl_delay_bar"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("m3", "via2", "m2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
@ -742,7 +743,11 @@ class control_logic(design.design):
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list
if props.dff_buff_array.add_body_contacts:
inst_pins.append("vpb")
inst_pins.append("vnb")
self.connect_inst(inst_pins)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0,0))
@ -754,14 +759,14 @@ class control_logic(design.design):
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2"))
# Connect the clock rail to the other clock rail
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = in_pos + vector(0,2*self.m2_pitch)
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, mid_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_wire(self.m1_stack,[in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
@ -786,7 +791,7 @@ class control_logic(design.design):
out_pin = inst.get_pin(pin_name)
right_pos=out_pin.center() + vector(self.width-out_pin.cx(),0)
self.add_layout_pin_segment_center(text=out_name,
layer="metal1",
layer="m1",
start=out_pin.center(),
end=right_pos)
@ -799,19 +804,19 @@ class control_logic(design.design):
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
if pin.layer == "metal1":
if pin.layer == "m1":
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
self.add_power_pin("vdd", pin_loc)
self.add_path("metal1", [row_loc, pin_loc])
self.add_path("m1", [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
if pin.layer == "metal1":
if pin.layer == "m1":
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
self.add_power_pin("gnd", pin_loc)
self.add_path("metal1", [row_loc, pin_loc])
self.add_path("m1", [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst,"gnd")
self.copy_layout_pin(self.delay_inst,"vdd")
@ -828,14 +833,14 @@ class control_logic(design.design):
"""
# pin=self.clk_inv1.get_pin("Z")
# self.add_label_pin(text="clk1_bar",
# layer="metal1",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
# pin=self.clk_inv2.get_pin("Z")
# self.add_label_pin(text="clk2",
# layer="metal1",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())

View File

@ -132,11 +132,11 @@ class delay_chain(design.design):
pin1_pos = pin1.center()
pin2_pos = pin2.center()
if pin1_pos.y == pin2_pos.y:
self.add_path("metal2", [pin1_pos, pin2_pos])
self.add_path("m2", [pin1_pos, pin2_pos])
else:
mid_point = vector(pin2_pos.x, 0.5*(pin1_pos.y+pin2_pos.y))
# Written this way to guarantee it goes right first if we are switching rows
self.add_path("metal2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos])
self.add_path("m2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos])
def route_inverters(self):
""" Add metal routing for each of the fanout stages """
@ -146,22 +146,22 @@ class delay_chain(design.design):
for load in self.load_inst_map[inv]:
# Drop a via on each A pin
a_pin = load.get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=a_pin.center())
# Route an M3 horizontal wire to the furthest
z_pin = inv.get_pin("Z")
a_pin = inv.get_pin("A")
a_max = self.rightest_load_inst[inv].get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=z_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=z_pin.center())
self.add_path("metal3",[z_pin.center(), a_max.center()])
self.add_path("m3",[z_pin.center(), a_max.center()])
# Route Z to the A of the next stage
@ -172,7 +172,7 @@ class delay_chain(design.design):
y_mid = (z_pin.cy() + next_a_pin.cy())/2
mid1_point = vector(z_pin.cx(), y_mid)
mid2_point = vector(next_a_pin.cx(), y_mid)
self.add_path("metal2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
self.add_path("m2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
def add_layout_pins(self):
@ -205,10 +205,10 @@ class delay_chain(design.design):
# input is A pin of first inverter
a_pin = self.driver_inst_list[0].get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
self.add_layout_pin(text="in",
layer="metal2",
layer="m2",
offset=a_pin.ll().scale(1,0),
height=a_pin.cy())
@ -216,12 +216,12 @@ class delay_chain(design.design):
# output is A pin of last load inverter
last_driver_inst = self.driver_inst_list[-1]
a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A")
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy())
self.add_path("metal2",[a_pin.center(), mid_point, mid_point.scale(1,0)])
self.add_path("m2",[a_pin.center(), mid_point, mid_point.scale(1,0)])
self.add_layout_pin_segment_center(text="out",
layer="metal2",
layer="m2",
start=mid_point,
end=mid_point.scale(1,0))

View File

@ -7,6 +7,7 @@
#
import design
from tech import GDS, layer, spice, parameter
from tech import cell_properties as props
import utils
@ -14,9 +15,15 @@ class dff(design.design):
"""
Memory address flip-flop
"""
if not props.dff.use_custom_ports:
pin_names = ["D", "Q", "clk", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
clk_pin = "clk"
else:
pin_names = props.dff.custom_port_list
type_list = props.dff.custom_type_list
clk_pin = props.dff.clk_pin
pin_names = ["D", "Q", "clk", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dff",
GDS["unit"],
layer["boundary"])

View File

@ -69,11 +69,12 @@ class dff_array(design.design):
name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col),
"clk",
"vdd",
"gnd"])
instance_ports = [self.get_din_name(row,col),
self.get_dout_name(row,col)]
for port in self.dff.pin_names:
if port != 'D' and port != 'Q':
instance_ports.append(port)
self.connect_inst(instance_ports)
def place_dff_array(self):
for row in range(self.rows):
@ -124,7 +125,7 @@ class dff_array(design.design):
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
@ -132,7 +133,7 @@ class dff_array(design.design):
height=din_pin.height())
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
@ -142,22 +143,22 @@ class dff_array(design.design):
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
clk_pin = self.dff_insts[0,0].get_pin(self.dff.clk_pin)
clk_ypos = 2*self.m3_pitch+self.m3_width
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2")
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
layer="m3",
start=vector(0,clk_ypos),
end=vector(self.width,clk_ypos))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
clk_pin = self.dff_insts[0,col].get_pin(self.dff.clk_pin)
# Make a vertical strip for each column
self.add_rect(layer="metal2",
self.add_rect(layer="m2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=vector(clk_pin.cx(),clk_ypos))
def get_clk_cin(self):

View File

@ -8,6 +8,7 @@
import debug
import design
from tech import drc,parameter
from tech import cell_properties as props
from math import log
from vector import vector
from globals import OPTS
@ -49,10 +50,11 @@ class dff_buf(design.design):
self.create_instances()
def create_layout(self):
self.width = self.dff.width + self.inv1.width + self.inv2.width
self.place_instances()
self.width = self.inv2_inst.rx()
self.height = self.dff.height
self.place_instances()
self.route_wires()
self.add_layout_pins()
self.add_boundary()
@ -82,10 +84,15 @@ class dff_buf(design.design):
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
if props.dff_buff.add_body_contacts:
self.add_pin("vpb", "INPUT")
self.add_pin("vpn", "INPUT")
def create_instances(self):
self.dff_inst=self.add_inst(name="dff_buf_dff",
mod=self.dff)
self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
self.connect_inst(props.dff_buff.buf_ports)
#self.connect_inst(["D", "qint", "clk", "vdd", "gnd"])
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
mod=self.inv1)
@ -100,7 +107,10 @@ class dff_buf(design.design):
self.dff_inst.place(vector(0,0))
# Add INV1 to the right
self.inv1_inst.place(vector(self.dff_inst.rx(),0))
well_spacing = max(self.nwell_space,
self.pwell_space,
self.pwell_to_nwell)
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active,0))
# Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
@ -112,12 +122,12 @@ class dff_buf(design.design):
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=self.m2_stack,
offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=a1_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a1_pin.center())
# Route inv1 z to inv2 a
@ -126,14 +136,14 @@ class dff_buf(design.design):
mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy())
mid2 = vector(mid_x_offset, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
self.add_path("m1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.dff_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
@ -141,7 +151,7 @@ class dff_buf(design.design):
# Continous gnd rail along with label.
gnd_pin=self.dff_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
@ -164,18 +174,18 @@ class dff_buf(design.design):
mid_pos = dout_pin.center() + vector(self.m1_pitch,0)
q_pos = mid_pos - vector(0,self.m2_pitch)
self.add_layout_pin_rect_center(text="Q",
layer="metal2",
layer="m2",
offset=q_pos)
self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_path("m1", [dout_pin.center(), mid_pos, q_pos])
self.add_via_center(layers=self.m1_stack,
offset=q_pos)
qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch)
self.add_layout_pin_rect_center(text="Qb",
layer="metal2",
layer="m2",
offset=qb_pos)
self.add_path("metal1", [self.mid_qb_pos, qb_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_path("m1", [self.mid_qb_pos, qb_pos])
self.add_via_center(layers=self.m1_stack,
offset=qb_pos)
def get_clk_cin(self):

View File

@ -8,6 +8,7 @@
import debug
import design
from tech import drc
from tech import cell_properties as props
from math import log
from vector import vector
from globals import OPTS
@ -64,12 +65,17 @@ class dff_buf_array(design.design):
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
if props.dff_buff_array.add_body_contacts:
self.add_pin("vpb", "INPUT")
self.add_pin("vnb", "INPUT")
def add_modules(self):
self.dff = factory.create(module_type="dff_buf",
inv1_size=self.inv1_size,
inv2_size=self.inv2_size)
self.add_mod(self.dff)
def create_dff_array(self):
self.dff_insts={}
for row in range(self.rows):
@ -77,22 +83,33 @@ class dff_buf_array(design.design):
name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),
inst_ports = [self.get_din_name(row,col),
self.get_dout_name(row,col),
self.get_dout_bar_name(row,col),
"clk",
"vdd",
"gnd"])
"gnd"]
if props.dff_buff_array.add_body_contacts:
inst_ports.append("vpb")
inst_ports.append("vnb")
self.connect_inst(inst_ports)
def place_dff_array(self):
well_spacing = max(self.nwell_space,
self.pwell_space,
self.pwell_to_nwell)
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
base = vector(col*dff_pitch,row*self.dff.height)
mirror = "R0"
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
base = vector(col*dff_pitch,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[row,col].place(offset=base,
mirror=mirror)
@ -142,7 +159,7 @@ class dff_buf_array(design.design):
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
@ -150,7 +167,7 @@ class dff_buf_array(design.design):
height=din_pin.height())
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
@ -158,7 +175,7 @@ class dff_buf_array(design.design):
height=dout_pin.height())
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
@ -169,28 +186,28 @@ class dff_buf_array(design.design):
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
clk_ypos = 2*self.m3_pitch+self.m3_width
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="metal2",
layer="m2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
layer="m3",
start=vector(0,clk_ypos),
end=vector(self.width,clk_ypos))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
# Make a vertical strip for each column
self.add_rect(layer="metal2",
self.add_rect(layer="m2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=vector(clk_pin.cx(),clk_ypos))
def get_clk_cin(self):

View File

@ -97,13 +97,13 @@ class dff_inv(design.design):
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3",
self.add_path("m3",
[q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=a1_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a1_pin.center())
@ -112,7 +112,7 @@ class dff_inv(design.design):
# Continous vdd rail along with label.
vdd_pin=self.dff_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll(),
width=self.width,
height=vdd_pin.height())
@ -120,7 +120,7 @@ class dff_inv(design.design):
# Continous gnd rail along with label.
gnd_pin=self.dff_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll(),
width=self.width,
height=vdd_pin.height())
@ -146,9 +146,9 @@ class dff_inv(design.design):
dout_pin = self.inv1_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Qb",
layer="metal2",
layer="m2",
offset=dout_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=dout_pin.center())
def get_clk_cin(self):

View File

@ -140,7 +140,7 @@ class dff_inv_array(design.design):
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
@ -148,7 +148,7 @@ class dff_inv_array(design.design):
height=din_pin.height())
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
@ -156,7 +156,7 @@ class dff_inv_array(design.design):
height=dout_pin.height())
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
@ -167,27 +167,27 @@ class dff_inv_array(design.design):
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
clk_ypos = 2*self.m3_pitch+self.m3_width
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="metal2",
layer="m2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
layer="m3",
start=vector(0,clk_ypos),
end=vector(self.width,clk_ypos))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
# Make a vertical strip for each column
self.add_rect(layer="metal2",
self.add_rect(layer="m2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_via_center(layers=self.m2_stack,
offset=vector(clk_pin.cx(),clk_ypos))
def get_clk_cin(self):

View File

@ -3,31 +3,22 @@
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
import debug
import design
from tech import drc
import contact
from bitcell_base_array import bitcell_base_array
from sram_factory import factory
from vector import vector
from globals import OPTS
class dummy_array(design.design):
class dummy_array(bitcell_base_array):
"""
Generate a dummy row/column for the replica array.
"""
def __init__(self, cols, rows, mirror=0, name=""):
design.design.__init__(self, name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.column_size = cols
self.row_size = rows
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""):
super().__init__(cols, rows, name, column_offset)
self.mirror = mirror
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
@ -37,27 +28,7 @@ class dummy_array(design.design):
def create_layout(self):
# We increase it by a well enclosure so the precharges don't overlap our wells
self.height = self.row_size*self.dummy_cell.height
self.width = self.column_size*self.dummy_cell.width
xoffset = 0.0
for col in range(self.column_size):
yoffset = 0.0
for row in range(self.row_size):
name = "dummy_r{0}_c{1}".format(row, col)
if (row+self.mirror) % 2:
tempy = yoffset + self.dummy_cell.height
dir_key = "MX"
else:
tempy = yoffset
dir_key = ""
self.cell_inst[row,col].place(offset=[xoffset, tempy],
mirror=dir_key)
yoffset += self.dummy_cell.height
xoffset += self.dummy_cell.width
self.place_array("dummy_r{0}_c{1}", self.mirror)
self.add_layout_pins()
@ -65,18 +36,6 @@ class dummy_array(design.design):
self.DRC_LVS()
def add_pins(self):
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
self.add_pin(cell_column+"_{0}".format(col), "INOUT")
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Add the modules used in this design """
self.dummy_cell = factory.create(module_type="dummy_bitcell")
@ -84,23 +43,6 @@ class dummy_array(design.design):
self.cell = factory.create(module_type="bitcell")
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """
bitcell_pins = []
pin_names = self.cell.get_all_bitline_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(col))
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin+"_{0}".format(row))
bitcell_pins.append("vdd")
bitcell_pins.append("gnd")
return bitcell_pins
def create_instances(self):
""" Create the module instances used in this design """
@ -111,39 +53,6 @@ class dummy_array(design.design):
self.cell_inst[row,col]=self.add_inst(name=name,
mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(col, row))
def add_layout_pins(self):
""" Add the layout pins """
row_list = self.cell.get_all_wl_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size):
for cell_column in column_list:
bl_pin = self.cell_inst[0,col].get_pin(cell_column)
self.add_layout_pin(text=cell_column+"_{0}".format(col),
layer="metal2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=self.height)
for row in range(self.row_size):
for cell_row in row_list:
wl_pin = self.cell_inst[row,0].get_pin(cell_row)
self.add_layout_pin(text=cell_row+"_{0}".format(row),
layer="metal1",
offset=wl_pin.ll(),
width=self.width,
height=wl_pin.height())
# For every second row and column, add a via for gnd and vdd
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
def input_load(self):
wl_wire = self.gen_wl_wire()

View File

@ -5,23 +5,19 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from tech import drc
import debug
import design
from math import log
from math import sqrt
from math import ceil
import math
import contact
from sram_factory import factory
from vector import vector
from globals import OPTS
class hierarchical_decoder(design.design):
"""
Dynamically generated hierarchical decoder.
"""
def __init__(self, name, rows, height=None):
def __init__(self, name, rows):
design.design.__init__(self, name)
self.NAND_FORMAT = "DEC_NAND_{0}"
@ -30,16 +26,16 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = []
self.pre3x8_inst = []
self.cell_height = height
b = factory.create(module_type="bitcell")
self.cell_height = b.height
self.rows = rows
self.num_inputs = math.ceil(math.log(self.rows, 2))
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.setup_netlist_constants()
@ -173,7 +169,7 @@ class hierarchical_decoder(design.design):
input_offset=vector(min_x - self.input_routing_width,0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
@ -221,11 +217,11 @@ class hierarchical_decoder(design.design):
def route_input_rail(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=input_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=output_offset)
self.add_path(("metal3"), [input_offset, output_offset])
self.add_path(("m3"), [input_offset, output_offset])
def add_pins(self):
@ -307,7 +303,6 @@ class hierarchical_decoder(design.design):
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.pre2x4_inst[num].place(base)
def place_pre3x8(self,num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
@ -320,7 +315,6 @@ class hierarchical_decoder(design.design):
self.pre3x8_inst[num].place(offset)
def create_row_decoder(self):
""" Create the row-decoder by placing NAND2/NAND3 and Inverters
and add the primary decoder output pins. """
@ -328,7 +322,6 @@ class hierarchical_decoder(design.design):
self.create_decoder_nand_array()
self.create_decoder_inv_array()
def create_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """
@ -467,11 +460,11 @@ class hierarchical_decoder(design.design):
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[row].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(row),
layer="metal1",
layer="m1",
offset=z_pin.ll(),
width=z_pin.width(),
height=z_pin.height())
@ -485,7 +478,7 @@ class hierarchical_decoder(design.design):
if (self.num_inputs >= 4):
input_offset = vector(0.5*self.m2_width,0)
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.predecode_rails = self.create_vertical_pin_bus(layer="metal2",
self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
@ -555,7 +548,7 @@ class hierarchical_decoder(design.design):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# The vias will be placed in the center and right of the cells, respectively.
xoffset = self.nand_inst[0].cx()
xoffset = self.nand_inst[0].rx()
for num in range(0,self.rows):
for pin_name in ["vdd", "gnd"]:
# The nand and inv are the same height rows...
@ -570,7 +563,7 @@ class hierarchical_decoder(design.design):
start = self.nand_inst[num].get_pin(pin_name).lc()
end = self.inv_inst[num].get_pin(pin_name).rc()
mid = (start+end).scale(0.5,0.5)
self.add_rect_center(layer="metal1",
self.add_rect_center(layer="m1",
offset=mid,
width=end.x-start.x)
@ -584,8 +577,8 @@ class hierarchical_decoder(design.design):
def route_predecode_rail(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_path("m1", [rail_pos, pin.lc()])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
@ -595,10 +588,10 @@ class hierarchical_decoder(design.design):
# It would be fixed with a channel router.
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
rail_pos = vector(self.predecode_rails[rail_name].x,mid_point.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=pin.center())
self.add_wire(("metal3","via2","metal2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_wire(("m3","via2","m2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
def input_load(self):

View File

@ -8,12 +8,11 @@
import debug
import design
import math
from tech import drc
import contact
from vector import vector
from globals import OPTS
from sram_factory import factory
class hierarchical_predecode(design.design):
"""
Pre 2x4 and 3x8 decoder shared code.
@ -42,7 +41,7 @@ class hierarchical_predecode(design.design):
self.add_nand(self.number_of_inputs)
self.add_mod(self.nand)
def add_nand(self,inputs):
def add_nand(self, inputs):
""" Create the NAND for the predecode input stage """
if inputs==2:
self.nand = factory.create(module_type="pnand2",
@ -51,7 +50,7 @@ class hierarchical_predecode(design.design):
self.nand = factory.create(module_type="pnand3",
height=self.cell_height)
else:
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
def setup_layout_constraints(self):
@ -73,7 +72,7 @@ class hierarchical_predecode(design.design):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5*self.m2_width,2*self.m1_width)
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=offset,
names=input_names,
@ -83,13 +82,12 @@ class hierarchical_predecode(design.design):
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width)
self.decode_rails = self.create_vertical_bus(layer="metal2",
self.decode_rails = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch,
offset=offset,
names=decode_names,
length=self.height - 2*self.m1_width)
def create_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """
self.in_inst = []
@ -178,15 +176,15 @@ class hierarchical_predecode(design.design):
# route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no
# pins in the nand gates.
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1m2.width + self.m1_space
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num)
in_pos = vector(self.input_rails[in_pin].x,y_offset)
a_pos = vector(self.decode_rails[a_pin].x,y_offset)
self.add_path("metal1",[in_pos, a_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
self.add_path("m1",[in_pos, a_pos])
self.add_via_center(layers = self.m1_stack,
offset=[self.input_rails[in_pin].x, y_offset])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
self.add_via_center(layers = self.m1_stack,
offset=[self.decode_rails[a_pin].x, y_offset])
def route_output_inverters(self):
@ -201,11 +199,11 @@ class hierarchical_predecode(design.design):
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[num].get_pin("Z")
self.add_layout_pin(text="out_{}".format(num),
layer="metal1",
layer="m1",
offset=z_pin.ll(),
height=z_pin.height(),
width=z_pin.width())
@ -226,16 +224,16 @@ class hierarchical_predecode(design.design):
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
rail_pos = vector(self.decode_rails[out_pin].x,y_offset)
self.add_path("metal1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_center(layers = ("metal1", "via1", "metal2"),
self.add_path("m1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_center(layers = self.m1_stack,
offset=rail_pos)
#route input
inv_in_pos = self.in_inst[inv_num].get_pin("A").lc()
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
self.add_path("metal1", [in_pos, inv_in_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_path("m1", [in_pos, inv_in_pos])
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
@ -255,8 +253,8 @@ class hierarchical_predecode(design.design):
for rail_pin,gate_pin in zip(index_lst,gate_lst):
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc()
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("metal1", [rail_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
@ -266,7 +264,7 @@ class hierarchical_predecode(design.design):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed
in_xoffset = self.in_inst[0].rx()
in_xoffset = self.in_inst[0].rx() + self.m1_space
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
@ -275,7 +273,7 @@ class hierarchical_predecode(design.design):
for n in ["vdd", "gnd"]:
nand_pin = self.nand_inst[num].get_pin(n)
supply_offset = nand_pin.ll().scale(0,1)
self.add_rect(layer="metal1",
self.add_rect(layer="m1",
offset=supply_offset,
width=self.inv_inst[num].rx())

View File

@ -0,0 +1,26 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
class module_type():
"""
This is a class that maps cell names to python classes implementing them.
"""
def __init__(self):
self.names = {}
def __setitem__(self, b, c):
self.names[b] = c
def is_overridden(self, b):
return (b in self.names.keys())
def __getitem__(self, b):
if b not in self.names.keys():
raise KeyError
return self.names[b]

View File

@ -160,7 +160,7 @@ class multibank(design.design):
self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclosure_active"),
self.m2_gap = max(2*drc("pwell_to_nwell"] + drc["well_enclose_active"),
2*self.m2_pitch)
@ -451,14 +451,14 @@ class multibank(design.design):
# Connect the inverter output to the central bus
out_pos = self.bank_select_inst.get_pin(gated_name).rc()
bus_pos = vector(self.bus_xoffset[gated_name], out_pos.y)
self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_path("m3",[out_pos, bus_pos])
self.add_via_center(layers=self.m2_stack,
offset=bus_pos,
rotate=90)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=out_pos,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=out_pos,
rotate=90)
@ -512,7 +512,7 @@ class multibank(design.design):
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, 0)
control_bus_length = self.bitcell_array_inst.uy()
self.bus_xoffset = self.create_vertical_bus(layer="metal2",
self.bus_xoffset = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals,
@ -530,9 +530,9 @@ class multibank(design.design):
bitcell_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).uc()
yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
self.add_path("m2",[precharge_bl, vector(precharge_bl.x,yoffset),
vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("metal2",[precharge_br, vector(precharge_br.x,yoffset),
self.add_path("m2",[precharge_br, vector(precharge_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br])
@ -550,9 +550,9 @@ class multibank(design.design):
bitcell_br = self.bitcell_array_inst.get_pin("br_{}".format(i)).bc()
yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y)
self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
self.add_path("m2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
self.add_path("m2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self):
@ -573,9 +573,9 @@ class multibank(design.design):
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
self.add_path("m2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
vector(connect_bl.x,yoffset), connect_bl])
self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
self.add_path("m2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
vector(connect_br.x,yoffset), connect_br])
def route_sense_amp_to_trigate(self):
@ -586,11 +586,11 @@ class multibank(design.design):
tri_gate_in = self.tri_gate_array_inst.get_pin("in_{}".format(i)).lc()
sa_data_out = self.sense_amp_array_inst.get_pin("data_{}".format(i)).bc()
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=tri_gate_in)
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=sa_data_out)
self.add_path("metal3",[sa_data_out,tri_gate_in])
self.add_path("m3",[sa_data_out,tri_gate_in])
def route_sense_amp_out(self):
""" Add pins for the sense amp output """
@ -644,14 +644,14 @@ class multibank(design.design):
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(i)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
# The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.wordline_driver_inst.get_pin("wl_{}".format(i)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin("wl_{}".format(i)).lc()
mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0)
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
self.add_path("m1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
@ -698,8 +698,8 @@ class multibank(design.design):
else:
mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y)
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
#self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
#self.add_wire(self.m1_stack,[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("m1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
@ -715,7 +715,7 @@ class multibank(design.design):
wl_name = "wl_{}".format(i)
wl_pin = self.bitcell_array_inst.get_pin(wl_name)
self.add_label(text=wl_name,
layer="metal1",
layer="m1",
offset=wl_pin.center())
# Add the bitline names
@ -725,10 +725,10 @@ class multibank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_name)
br_pin = self.bitcell_array_inst.get_pin(br_name)
self.add_label(text=bl_name,
layer="metal2",
layer="m2",
offset=bl_pin.center())
self.add_label(text=br_name,
layer="metal2",
layer="m2",
offset=br_pin.center())
# # Add the data output names to the sense amp output
@ -736,7 +736,7 @@ class multibank(design.design):
# data_name = "data_{}".format(i)
# data_pin = self.sense_amp_array_inst.get_pin(data_name)
# self.add_label(text="sa_out_{}".format(i),
# layer="metal2",
# layer="m2",
# offset=data_pin.center())
# Add labels on the decoder
@ -745,7 +745,7 @@ class multibank(design.design):
pin_name = "in_{}".format(i)
data_pin = self.wordline_driver_inst.get_pin(pin_name)
self.add_label(text=data_name,
layer="metal1",
layer="m1",
offset=data_pin.center())
@ -765,8 +765,8 @@ class multibank(design.design):
for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_path("m1", [control_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=control_pos,
rotate=90)
@ -776,9 +776,9 @@ class multibank(design.design):
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[control_signal].x
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
self.add_wire(self.m1_stack,[pin_pos, mid_pos, control_pos])
control_via_pos = vector(control_x_offset, mid_pos.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=control_via_pos,
rotate=90)
@ -791,13 +791,13 @@ class multibank(design.design):
if self.num_banks > 1:
# it's not an input pin if we have multiple banks
self.add_label_pin(text=ctrl,
layer="metal2",
layer="m2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.max_y_offset-self.min_y_offset)
else:
self.add_layout_pin(text=ctrl,
layer="metal2",
layer="m2",
offset=vector(x_offset, self.min_y_offset),
width=self.m2_width,
height=self.max_y_offset-self.min_y_offset)
@ -807,12 +807,12 @@ class multibank(design.design):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
# Bring it up to M2 for M2/M3 routing
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + contact.m1m2.offset,
self.add_via(layers=self.m1_stack,
offset=in_pin + contact.m1_via.offset,
rotate=90)
self.add_via(layers=("metal2","via2","metal3"),
self.add_via(layers=self.m2_stack,
offset=in_pin + self.m2m3_via_offset,
rotate=90)
@ -821,10 +821,10 @@ class multibank(design.design):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pin = inst.get_pin(pin).rc()
rail_pos = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + contact.m1m2.offset,
self.add_wire(("m3","via2","m2"),[in_pin, rail_pos, rail_pos - vector(0,self.m2_pitch)])
self.add_via(layers=self.m1_stack,
offset=in_pin + contact.m1_via.offset,
rotate=90)
self.add_via(layers=("metal2","via2","metal3"),
self.add_via(layers=self.m2_stack,
offset=in_pin + self.m2m3_via_offset,
rotate=90)

View File

@ -92,7 +92,7 @@ class port_address(design.design):
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
@ -149,7 +149,7 @@ class port_address(design.design):
"""
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
row_decoder_offset = vector(0,0)

View File

@ -38,6 +38,15 @@ class port_data(design.design):
self.create_layout()
self.add_boundary()
def get_bl_names(self):
# bl lines are connect from the precharger
return self.precharge.get_bl_names()
def get_br_names(self):
# br lines are connect from the precharger
return self.precharge.get_br_names()
def create_netlist(self):
self.precompute_constants()
@ -85,8 +94,10 @@ class port_data(design.design):
self.add_pin("rbl_bl","INOUT")
self.add_pin("rbl_br","INOUT")
for bit in range(self.num_cols):
self.add_pin("{0}_{1}".format(self.bl_names[self.port], bit),"INOUT")
self.add_pin("{0}_{1}".format(self.br_names[self.port], bit),"INOUT")
bl_name = self.precharge_array.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port)
self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT")
self.add_pin("{0}_{1}".format(br_name, bit),"INOUT")
if self.port in self.read_ports:
for bit in range(self.word_size):
self.add_pin("dout_{}".format(bit),"OUTPUT")
@ -212,7 +223,7 @@ class port_data(design.design):
# A space for wells or jogging m2 between modules
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
@ -221,6 +232,18 @@ class port_data(design.design):
self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names()
# used for bl/br names
self.precharge = factory.create(module_type="precharge",
bitcell_bl = self.bl_names[0],
bitcell_br = self.br_names[0])
# We create a dummy here to get bl/br names to add those pins to this
# module, which happens before we create the real precharge_array
self.precharge_array = factory.create(module_type="precharge_array",
columns=self.num_cols + 1,
bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port])
def create_precharge_array(self):
""" Creating Precharge """
@ -230,6 +253,8 @@ class port_data(design.design):
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
mod=self.precharge_array)
bl_name = self.precharge_array.get_bl_name(self.port)
br_name = self.precharge_array.get_br_name(self.port)
temp = []
# Use left BLs for RBL
@ -237,8 +262,9 @@ class port_data(design.design):
temp.append("rbl_bl")
temp.append("rbl_br")
for bit in range(self.num_cols):
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
temp.append(self.br_names[self.port]+"_{0}".format(bit))
temp.append("{0}_{1}".format(bl_name, bit))
temp.append("{0}_{1}".format(br_name, bit))
# Use right BLs for RBL
if self.port==1:
temp.append("rbl_bl")
@ -259,15 +285,19 @@ class port_data(design.design):
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
mod=self.column_mux_array)
bl_name = self.column_mux_array.get_bl_name(self.port)
br_name = self.column_mux_array.get_br_name(self.port)
temp = []
for col in range(self.num_cols):
temp.append(self.bl_names[self.port]+"_{0}".format(col))
temp.append(self.br_names[self.port]+"_{0}".format(col))
temp.append("{0}_{1}".format(bl_name, col))
temp.append("{0}_{1}".format(br_name, col))
for word in range(self.words_per_row):
temp.append("sel_{}".format(word))
for bit in range(self.word_size):
temp.append(self.bl_names[self.port]+"_out_{0}".format(bit))
temp.append(self.br_names[self.port]+"_out_{0}".format(bit))
temp.append("{0}_out_{1}".format(bl_name, bit))
temp.append("{0}_out_{1}".format(br_name, bit))
temp.append("gnd")
self.connect_inst(temp)
@ -285,15 +315,18 @@ class port_data(design.design):
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
mod=self.sense_amp_array)
bl_name = self.sense_amp_array.get_bl_name(self.port)
br_name = self.sense_amp_array.get_br_name(self.port)
temp = []
for bit in range(self.word_size):
temp.append("dout_{}".format(bit))
if self.words_per_row == 1:
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
temp.append(self.br_names[self.port]+"_{0}".format(bit))
temp.append("{0}_{1}".format(bl_name, bit))
temp.append("{0}_{1}".format(br_name, bit))
else:
temp.append(self.bl_names[self.port]+"_out_{0}".format(bit))
temp.append(self.br_names[self.port]+"_out_{0}".format(bit))
temp.append("{0}_out_{1}".format(bl_name, bit))
temp.append("{0}_out_{1}".format(br_name, bit))
temp.extend(["s_en", "vdd", "gnd"])
self.connect_inst(temp)
@ -308,6 +341,8 @@ class port_data(design.design):
""" Creating Write Driver """
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
mod=self.write_driver_array)
bl_name = self.write_driver_array.get_bl_name(self.port)
br_name = self.write_driver_array.get_br_name(self.port)
temp = []
for bit in range(self.word_size):
@ -315,11 +350,11 @@ class port_data(design.design):
for bit in range(self.word_size):
if (self.words_per_row == 1):
temp.append(self.bl_names[self.port] + "_{0}".format(bit))
temp.append(self.br_names[self.port] + "_{0}".format(bit))
temp.append("{0}_{1}".format(bl_name, bit))
temp.append("{0}_{1}".format(br_name, bit))
else:
temp.append(self.bl_names[self.port] + "_out_{0}".format(bit))
temp.append(self.br_names[self.port] + "_out_{0}".format(bit))
temp.append("{0}_out_{1}".format(bl_name, bit))
temp.append("{0}_out_{1}".format(br_name, bit))
if self.write_size is not None:
for i in range(self.num_wmasks):
@ -475,11 +510,11 @@ class port_data(design.design):
end_pos = vector(length, wdriver_en_pin.cy())
# Add via for the write driver array's enable input
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=end_pos)
# Route between write mask AND array and write driver array
self.add_wire(("metal1","via1","metal2"), [beg_pos, middle_pos, end_pos])
self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos])
def route_column_mux_to_precharge_array(self, port):
@ -626,7 +661,7 @@ class port_data(design.design):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))]
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset)
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitlines(self, inst1, inst2, num_bits,
@ -655,9 +690,9 @@ class port_data(design.design):
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset),
self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset),
vector(top_bl.x,yoffset), top_bl])
self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset),
self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset),
vector(top_br.x,yoffset), top_br])
def graph_exclude_precharge(self):

View File

@ -32,6 +32,21 @@ class precharge_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
bl_name = self.pc_cell.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0):
br_name = self.pc_cell.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def add_pins(self):
"""Adds pins for spice file"""
for i in range(self.columns):
@ -68,10 +83,10 @@ class precharge_array(design.design):
def add_layout_pins(self):
self.add_layout_pin(text="en_bar",
layer="metal1",
layer="m1",
offset=self.pc_cell.get_pin("en_bar").ll(),
width=self.width,
height=drc("minwidth_metal1"))
height=drc("minwidth_m1"))
for inst in self.local_insts:
self.copy_layout_pin(inst, "vdd")
@ -80,15 +95,15 @@ class precharge_array(design.design):
inst = self.local_insts[i]
bl_pin = inst.get_pin("bl")
self.add_layout_pin(text="bl_{0}".format(i),
layer="metal2",
layer="m2",
offset=bl_pin.ll(),
width=drc("minwidth_metal2"),
width=drc("minwidth_m2"),
height=bl_pin.height())
br_pin = inst.get_pin("br")
self.add_layout_pin(text="br_{0}".format(i),
layer="metal2",
layer="m2",
offset=br_pin.ll(),
width=drc("minwidth_metal2"),
width=drc("minwidth_m2"),
height=bl_pin.height())
@ -107,9 +122,19 @@ class precharge_array(design.design):
def place_insts(self):
""" Places precharge array by horizontally tiling the precharge cell"""
from tech import cell_properties
xoffset = 0
for i in range(self.columns):
offset = vector(self.pc_cell.width * i, 0)
self.local_insts[i].place(offset)
tempx = xoffset
if cell_properties.bitcell.mirror.y and (i + 1) % 2:
mirror = "MY"
tempx = tempx + self.pc_cell.width
else:
mirror = ""
offset = vector(tempx, 0)
self.local_insts[i].place(offset=offset, mirror=mirror)
xoffset = xoffset + self.pc_cell.width
def get_en_cin(self):
"""Get the relative capacitance of all the clk connections in the precharge array"""

View File

@ -10,10 +10,7 @@ from tech import drc, spice
from vector import vector
from globals import OPTS
from sram_factory import factory
import logical_effort
import bitcell_array
import replica_column
import dummy_array
class replica_bitcell_array(design.design):
"""
@ -86,6 +83,7 @@ class replica_bitcell_array(design.design):
# Bitcell array
self.bitcell_array = factory.create(module_type="bitcell_array",
column_offset=1 + self.left_rbl,
cols=self.column_size,
rows=self.row_size)
self.add_mod(self.bitcell_array)
@ -95,12 +93,17 @@ class replica_bitcell_array(design.design):
for bit in range(self.left_rbl+self.right_rbl):
if bit<self.left_rbl:
replica_bit = bit+1
# dummy column
column_offset = 1
else:
replica_bit = bit+self.row_size+1
# dummy column + replica column + bitcell colums
column_offset = 3 + self.row_size
self.replica_columns[bit] = factory.create(module_type="replica_column",
rows=self.row_size,
left_rbl=self.left_rbl,
right_rbl=self.right_rbl,
column_offset=column_offset,
replica_bit=replica_bit)
self.add_mod(self.replica_columns[bit])
@ -108,24 +111,37 @@ class replica_bitcell_array(design.design):
self.dummy_row = factory.create(module_type="dummy_array",
cols=self.column_size,
rows=1,
# dummy column + left replica column
column_offset=1 + self.left_rbl,
mirror=0)
self.add_mod(self.dummy_row)
# Dummy col (mirror starting at first if odd replica+dummy rows)
self.dummy_col = factory.create(module_type="dummy_array",
cols=1,
rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl+1)%2)
self.add_mod(self.dummy_col)
self.dummy_col_left = factory.create(module_type="dummy_array",
cols=1,
column_offset=0,
rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl+1)%2)
self.add_mod(self.dummy_col_left)
self.dummy_col_right = factory.create(module_type="dummy_array",
cols=1,
# dummy column
# + left replica column
# + bitcell columns
# + right replica column
column_offset=1 + self.left_rbl + self.column_size + self.right_rbl,
rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl+1)%2)
self.add_mod(self.dummy_col_right)
def add_pins(self):
self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names()
self.bitcell_array_bl_names = self.bitcell_array.get_all_bitline_names()
self.bitcell_array_wl_names = [x for x in self.bitcell_array.pins if x.startswith("w")]
self.bitcell_array_bl_names = [x for x in self.bitcell_array.pins if x.startswith("b")]
# These are the non-indexed names
# These are the non-indexed names
self.dummy_cell_wl_names = ["dummy_"+x for x in self.cell.get_all_wl_names()]
self.dummy_cell_bl_names = ["dummy_"+x for x in self.cell.get_all_bitline_names()]
self.dummy_row_bl_names = self.bitcell_array_bl_names
@ -141,7 +157,7 @@ class replica_bitcell_array(design.design):
# Left port WLs (one dummy for each port when we allow >1 port)
for port in range(self.left_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))]
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))]
# Keep track of the pin that is the RBL
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
self.replica_col_wl_names.extend(wl_names)
@ -150,7 +166,7 @@ class replica_bitcell_array(design.design):
# Right port WLs (one dummy for each port when we allow >1 port)
for port in range(self.left_rbl,self.left_rbl+self.right_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.all_ports))]
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x),port) for x in range(len(self.cell.get_all_wl_names()))]
# Keep track of the pin that is the RBL
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
self.replica_col_wl_names.extend(wl_names)
@ -236,10 +252,10 @@ class replica_bitcell_array(design.design):
# Left/right Dummy columns
self.dummy_col_left_inst=self.add_inst(name="dummy_col_left",
mod=self.dummy_col)
mod=self.dummy_col_left)
self.connect_inst([x+"_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
self.dummy_col_right_inst=self.add_inst(name="dummy_col_right",
mod=self.dummy_col)
mod=self.dummy_col_right)
self.connect_inst([x+"_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies)
@ -300,22 +316,24 @@ class replica_bitcell_array(design.design):
# Main array wl and bl/br
pin_names = self.bitcell_array.get_pin_names()
for pin_name in pin_names:
if pin_name.startswith("wl"):
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(0,1),
width=self.width,
height=pin.height())
elif pin_name.startswith("bl") or pin_name.startswith("br"):
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(1,0),
width=pin.width(),
height=self.height)
for wl in self.bitcell_array_wl_names:
if wl in pin_name:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(0,1),
width=self.width,
height=pin.height())
for bitline in self.bitcell_array_bl_names:
if bitline in pin_name:
pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list:
self.add_layout_pin(text=pin_name,
layer=pin.layer,
offset=pin.ll().scale(1,0),
width=pin.width(),
height=self.height)
# Replica wordlines
@ -400,7 +418,7 @@ class replica_bitcell_array(design.design):
else:
height = self.height
bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_metal1"))
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1"))
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
return bl_wire

View File

@ -20,7 +20,8 @@ class replica_column(design.design):
replica cell.
"""
def __init__(self, name, rows, left_rbl, right_rbl, replica_bit):
def __init__(self, name, rows, left_rbl, right_rbl, replica_bit,
column_offset=0):
design.design.__init__(self, name)
self.rows = rows
@ -29,6 +30,7 @@ class replica_column(design.design):
self.replica_bit = replica_bit
# left, right, regular rows plus top/bottom dummy cells
self.total_size = self.left_rbl+rows+self.right_rbl+2
self.column_offset = column_offset
debug.check(replica_bit!=0 and replica_bit!=rows,"Replica bit cannot be the dummy row.")
debug.check(replica_bit<=left_rbl or replica_bit>=self.total_size-right_rbl-1,
@ -92,18 +94,35 @@ class replica_column(design.design):
self.connect_inst(self.get_bitcell_pins(0, row))
def place_instances(self):
from tech import cell_properties
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom
# so that we will start with mirroring rather than not mirroring
rbl_offset = (self.left_rbl+1)%2
# if our bitcells are mirrored on the y axis, check if we are in global
# column that needs to be flipped.
dir_y = False
xoffset = 0
if cell_properties.bitcell.mirror.y and self.column_offset % 2:
dir_y = True
xoffset = self.replica_cell.width
for row in range(self.total_size):
dir_x = False
name = "bit_r{0}_{1}".format(row,"rbl")
offset = vector(0,self.cell.height*(row+(row+rbl_offset)%2))
if (row+rbl_offset)%2:
if cell_properties.bitcell.mirror.x and (row+rbl_offset)%2:
dir_x = True
offset = vector(xoffset,self.cell.height*(row+(row+rbl_offset)%2))
if dir_x and dir_y:
dir_key = "XY"
elif dir_x:
dir_key = "MX"
elif dir_y:
dir_key = "MY"
else:
dir_key = "R0"
dir_key = ""
self.cell_inst[row].place(offset=offset,
mirror=dir_key)
@ -116,7 +135,7 @@ class replica_column(design.design):
for bl_name in self.cell.get_all_bitline_names():
bl_pin = self.cell_inst[0].get_pin(bl_name)
self.add_layout_pin(text=bl_name,
layer="metal2",
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=self.height)
@ -125,7 +144,7 @@ class replica_column(design.design):
for wl_name in self.cell.get_all_wl_names():
wl_pin = self.cell_inst[row].get_pin(wl_name)
self.add_layout_pin(text="{0}_{1}".format(wl_name,row),
layer="metal1",
layer="m1",
offset=wl_pin.ll().scale(0,1),
width=self.width,
height=wl_pin.height())

View File

@ -9,6 +9,7 @@ import design
import debug
import utils
from tech import GDS,layer, parameter,drc
from globals import OPTS
import logical_effort
class sense_amp(design.design):
@ -21,8 +22,18 @@ class sense_amp(design.design):
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"])
else:
(width, height) = (0,0)
pin_map = []
def get_bl_names(self):
return "bl"
def get_br_names(self):
return "br"
def __init__(self, name):
design.design.__init__(self, name)

View File

@ -34,6 +34,20 @@ class sense_amp_array(design.design):
self.create_layout()
def get_bl_name(self, port=0):
bl_name = self.amp.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0):
br_name = self.amp.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def create_netlist(self):
self.add_modules()
self.add_pins()
@ -84,50 +98,60 @@ class sense_amp_array(design.design):
"en", "vdd", "gnd"])
def place_sense_amp_array(self):
from tech import cell_properties
if self.bitcell.width > self.amp.width:
amp_spacing = self.bitcell.width * self.words_per_row
else:
amp_spacing = self.amp.width * self.words_per_row
for i in range(0,self.word_size):
amp_position = vector(amp_spacing * i, 0)
self.local_insts[i].place(amp_position)
xoffset = amp_spacing * i
# align the xoffset to the grid of bitcells. This way we
# know when to do the mirroring.
grid_x = int(xoffset / self.amp.width)
if cell_properties.bitcell.mirror.y and grid_x % 2:
mirror = "MY"
xoffset = xoffset + self.amp.width
else:
mirror = ""
amp_position = vector(xoffset, 0)
self.local_insts[i].place(offset=amp_position,mirror=mirror)
def add_layout_pins(self):
for i in range(len(self.local_insts)):
inst = self.local_insts[i]
gnd_pos = inst.get_pin("gnd").center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=gnd_pos)
self.add_layout_pin_rect_center(text="gnd",
layer="metal3",
offset=gnd_pos)
vdd_pos = inst.get_pin("vdd").center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vdd_pos)
self.add_layout_pin_rect_center(text="vdd",
layer="metal3",
offset=vdd_pos)
self.add_power_pin(name = "gnd",
loc = inst.get_pin("gnd").center(),
start_layer="m2",
vertical=True)
self.add_power_pin(name = "vdd",
loc = inst.get_pin("vdd").center(),
start_layer="m2",
vertical=True)
bl_pin = inst.get_pin("bl")
br_pin = inst.get_pin("br")
dout_pin = inst.get_pin("dout")
self.add_layout_pin(text="bl_{0}".format(i),
layer="metal2",
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
self.add_layout_pin(text="br_{0}".format(i),
layer="metal2",
layer="m2",
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
self.add_layout_pin(text="data_{0}".format(i),
layer="metal2",
layer="m2",
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
@ -137,10 +161,10 @@ class sense_amp_array(design.design):
# add sclk rail across entire array
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
self.add_layout_pin(text="en",
layer="metal1",
layer="m1",
offset=sclk_offset,
width=self.width,
height=drc("minwidth_metal1"))
height=drc("minwidth_m1"))
def input_load(self):
return self.amp.input_load()

View File

@ -37,6 +37,21 @@ class single_level_column_mux_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
bl_name = self.mux.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0):
br_name = self.mux.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def create_netlist(self):
self.add_modules()
self.add_pins()
@ -99,11 +114,19 @@ class single_level_column_mux_array(design.design):
"gnd"])
def place_array(self):
from tech import cell_properties
# For every column, add a pass gate
for col_num in range(self.columns):
xoffset = col_num * self.mux.width
if cell_properties.bitcell.mirror.y and col_num % 2:
mirror = "MY"
xoffset = xoffset + self.mux.width
else:
mirror = ""
name = "XMUX{0}".format(col_num)
x_off = vector(col_num * self.mux.width, self.route_height)
self.mux_inst[col_num].place(x_off)
offset = vector(xoffset, self.route_height)
self.mux_inst[col_num].place(offset=offset, mirror=mirror)
def add_layout_pins(self):
@ -113,13 +136,13 @@ class single_level_column_mux_array(design.design):
mux_inst = self.mux_inst[col_num]
offset = mux_inst.get_pin("bl").ll()
self.add_layout_pin(text="bl_{}".format(col_num),
layer="metal2",
layer="m2",
offset=offset,
height=self.height-offset.y)
offset = mux_inst.get_pin("br").ll()
self.add_layout_pin(text="br_{}".format(col_num),
layer="metal2",
layer="m2",
offset=offset,
height=self.height-offset.y)
@ -137,7 +160,7 @@ class single_level_column_mux_array(design.design):
for j in range(self.words_per_row):
offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch)
self.add_layout_pin(text="sel_{}".format(j),
layer="metal1",
layer="m1",
offset=offset,
width=self.mux.width * self.columns)
@ -155,12 +178,13 @@ class single_level_column_mux_array(design.design):
# use the y offset from the sel pin and the x offset from the gate
offset = vector(gate_offset.x,self.get_pin("sel_{}".format(sel_index)).cy())
# Add the poly contact with a shift to account for the rotation
self.add_via_center(layers=("metal1", "contact", "poly"),
self.add_via_center(layers=("m1", "contact", "poly"),
offset=offset)
self.add_path("poly", [offset, gate_offset])
def route_bitlines(self):
""" Connect the output bit-lines to form the appropriate width mux """
from tech import cell_properties
for j in range(self.columns):
bl_offset = self.mux_inst[j].get_pin("bl_out").bc()
br_offset = self.mux_inst[j].get_pin("br_out").bc()
@ -171,43 +195,58 @@ class single_level_column_mux_array(design.design):
bl_out_offset_end = bl_out_offset + vector(0,self.route_height)
br_out_offset_end = br_out_offset + vector(0,self.route_height)
if cell_properties.bitcell.mirror.y and j % 2:
tmp_bl_out_end = br_out_offset_end
tmp_br_out_end = bl_out_offset_end
else:
tmp_bl_out_end = bl_out_offset_end
tmp_br_out_end = br_out_offset_end
if (j % self.words_per_row) == 0:
# Create the metal1 to connect the n-way mux output from the pass gate
# These will be located below the select lines. Yes, these are M2 width
# to ensure vias are enclosed and M1 min width rules.
width = self.m2_width + self.mux.width * (self.words_per_row - 1)
self.add_path("metal1", [bl_out_offset, bl_out_offset+vector(width,0)])
self.add_path("metal1", [br_out_offset, br_out_offset+vector(width,0)])
if cell_properties.bitcell.mirror.y and (j % 2) == 0:
bl = self.mux.get_pin("bl")
br = self.mux.get_pin("br")
dist = abs(bl.ll().x - br.ll().x)
else:
dist = 0
self.add_path("m1", [bl_out_offset, bl_out_offset+vector(width+dist,0)])
self.add_path("m1", [br_out_offset, br_out_offset+vector(width-dist,0)])
# Extend the bitline output rails and gnd downward on the first bit of each n-way mux
self.add_layout_pin_segment_center(text="bl_out_{}".format(int(j/self.words_per_row)),
layer="metal2",
layer="m2",
start=bl_out_offset,
end=bl_out_offset_end)
end=tmp_bl_out_end)
self.add_layout_pin_segment_center(text="br_out_{}".format(int(j/self.words_per_row)),
layer="metal2",
layer="m2",
start=br_out_offset,
end=br_out_offset_end)
end=tmp_br_out_end)
# This via is on the right of the wire
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=bl_out_offset)
# This via is on the left of the wire
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=br_out_offset)
else:
self.add_path("metal2", [ bl_out_offset, bl_out_offset_end])
self.add_path("metal2", [ br_out_offset, br_out_offset_end])
self.add_path("m2", [ bl_out_offset, tmp_bl_out_end])
self.add_path("m2", [ br_out_offset, tmp_br_out_end])
# This via is on the right of the wire
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=bl_out_offset)
# This via is on the left of the wire
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=br_out_offset)
def get_drain_cin(self):

View File

@ -83,14 +83,14 @@ class tri_gate_array(design.design):
in_pin = self.tri_inst[i].get_pin("in")
self.add_layout_pin(text="in_{0}".format(index),
layer="metal2",
layer="m2",
offset=in_pin.ll(),
width=in_pin.width(),
height=in_pin.height())
out_pin = self.tri_inst[i].get_pin("out")
self.add_layout_pin(text="out_{0}".format(index),
layer="metal2",
layer="m2",
offset=out_pin.ll(),
width=out_pin.width(),
height=out_pin.height())
@ -100,24 +100,24 @@ class tri_gate_array(design.design):
for n in ["vdd", "gnd"]:
for supply_pin in self.tri_inst[i].get_pins(n):
pin_pos = supply_pin.center()
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=pin_pos)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
layer="m3",
offset=pin_pos)
width = self.tri.width * self.columns - (self.words_per_row - 1) * self.tri.width
en_pin = self.tri_inst[0].get_pin("en")
self.add_layout_pin(text="en",
layer="metal1",
layer="m1",
offset=en_pin.ll().scale(0, 1),
width=width,
height=drc("minwidth_metal1"))
height=drc("minwidth_m1"))
enbar_pin = self.tri_inst[0].get_pin("en_bar")
self.add_layout_pin(text="en_bar",
layer="metal1",
layer="m1",
offset=enbar_pin.ll().scale(0, 1),
width=width,
height=drc("minwidth_metal1"))
height=drc("minwidth_m1"))

View File

@ -56,12 +56,16 @@ class wordline_driver(design.design):
self.add_pin("gnd", "GROUND")
def add_modules(self):
b = factory.create(module_type="bitcell")
self.inv = factory.create(module_type="pdriver",
fanout=self.cols,
neg_polarity=True)
neg_polarity=True,
height=b.height)
self.add_mod(self.inv)
self.nand2 = factory.create(module_type="pnand2")
self.nand2 = factory.create(module_type="pnand2",
height=b.height)
self.add_mod(self.nand2)
def route_vdd_gnd(self):
@ -142,7 +146,7 @@ class wordline_driver(design.design):
# Wordline enable connection
en_offset = [self.m1_width + 2 * self.m1_space, 0]
en_pin = self.add_layout_pin(text="en",
layer="metal2",
layer="m2",
offset=en_offset,
width=self.m2_width,
height=self.height)
@ -155,10 +159,10 @@ class wordline_driver(design.design):
a_pin = nand_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x, a_pos.y)
self.add_segment_center(layer="metal1",
self.add_segment_center(layer="m1",
start=clk_offset,
end=a_pos)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=clk_offset)
# Nand2 out to 2nd inv
@ -167,7 +171,7 @@ class wordline_driver(design.design):
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("metal1", [zr_pos, mid1_pos, mid2_pos, al_pos])
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
# connect the decoder input pin to nand2 B
b_pin = nand_inst.get_pin("B")
@ -177,29 +181,29 @@ class wordline_driver(design.design):
up_or_down = self.m2_space if row % 2 else -self.m2_space
input_offset = vector(0, b_pos.y + up_or_down)
base_offset = vector(clk_offset.x, input_offset.y)
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1m2.width, 0)
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
mid_via_offset = base_offset + contact_offset
# must under the clk line in M1
self.add_layout_pin_segment_center(text="in_{0}".format(row),
layer="metal1",
layer="m1",
start=input_offset,
end=mid_via_offset)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=mid_via_offset,
directions=("V", "V"))
# now connect to the nand2 B
self.add_path("metal2", [mid_via_offset, b_pos])
contact_offset = b_pos - vector(0.5 * contact.m1m2.height, 0)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_path("m2", [mid_via_offset, b_pos])
contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0)
self.add_via_center(layers=self.m1_stack,
offset=contact_offset,
directions=("H", "H"))
# output each WL on the right
wl_offset = inv2_inst.get_pin("Z").rc()
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
layer="metal1",
layer="m1",
start=wl_offset,
end=wl_offset - vector(self.m1_width, 0))

View File

@ -8,6 +8,7 @@
import debug
import design
import utils
from globals import OPTS
from tech import GDS,layer
class write_driver(design.design):
@ -20,8 +21,12 @@ class write_driver(design.design):
pin_names = ["din", "bl", "br", "en", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"])
if not OPTS.netlist_only:
(width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "write_driver", GDS["unit"])
else:
(width,height) = (0,0)
pin_map = []
def __init__(self, name):
design.design.__init__(self, name)
@ -32,6 +37,12 @@ class write_driver(design.design):
self.pin_map = write_driver.pin_map
self.add_pin_types(self.type_list)
def get_bl_names(self):
return "bl"
def get_br_names(self):
return "br"
def get_w_en_cin(self):
"""Get the relative capacitance of a single input"""
# This is approximated from SCMOS. It has roughly 5 3x transistor gates.

View File

@ -37,6 +37,19 @@ class write_driver_array(design.design):
if not OPTS.netlist_only:
self.create_layout()
def get_bl_name(self, port=0):
bl_name = self.driver.get_bl_names()
if len(self.all_ports) == 1:
return bl_name
else:
return bl_name + "{}".format(port)
def get_br_name(self, port=0):
br_name = self.driver.get_br_names()
if len(self.all_ports) == 1:
return br_name
else:
return br_name + "{}".format(port)
def create_netlist(self):
self.add_modules()
@ -106,34 +119,43 @@ class write_driver_array(design.design):
def place_write_array(self):
from tech import cell_properties
if self.bitcell.width > self.driver.width:
self.driver_spacing = self.bitcell.width
else:
self.driver_spacing = self.driver.width
for i in range(0,self.columns,self.words_per_row):
index = int(i/self.words_per_row)
base = vector(i * self.driver_spacing, 0)
self.driver_insts[index].place(base)
xoffset = i * self.driver_spacing
if cell_properties.bitcell.mirror.y and i % 2:
mirror = "MY"
xoffset = xoffset + self.driver.width
else:
mirror = ""
base = vector(xoffset, 0)
self.driver_insts[index].place(offset=base, mirror=mirror)
def add_layout_pins(self):
for i in range(self.word_size):
din_pin = self.driver_insts[i].get_pin("din")
self.add_layout_pin(text="data_{0}".format(i),
layer="metal2",
layer="m2",
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
bl_pin = self.driver_insts[i].get_pin("bl")
self.add_layout_pin(text="bl_{0}".format(i),
layer="metal2",
layer="m2",
offset=bl_pin.ll(),
width=bl_pin.width(),
height=bl_pin.height())
br_pin = self.driver_insts[i].get_pin("br")
self.add_layout_pin(text="br_{0}".format(i),
layer="metal2",
layer="m2",
offset=br_pin.ll(),
width=br_pin.width(),
height=br_pin.height())
@ -141,13 +163,10 @@ class write_driver_array(design.design):
for n in ["vdd", "gnd"]:
pin_list = self.driver_insts[i].get_pins(n)
for pin in pin_list:
pin_pos = pin.center()
# Add the M2->M3 stack
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=pin_pos)
self.add_layout_pin_rect_center(text=n,
layer="metal3",
offset=pin_pos)
self.add_power_pin(name = n,
loc = pin.center(),
vertical=True,
start_layer = "m2")
if self.write_size:
for bit in range(self.num_wmasks):
en_pin = self.driver_insts[bit*self.write_size].get_pin("en")
@ -165,7 +184,7 @@ class write_driver_array(design.design):
height=en_pin.height())
else:
self.add_layout_pin(text="en",
layer="metal1",
layer="m1",
offset=self.driver_insts[0].get_pin("en").ll().scale(0,1),
width=self.width)

View File

@ -104,8 +104,6 @@ class write_mask_and_array(design.design):
def add_layout_pins(self):
self.nand2 = factory.create(module_type="pnand2")
supply_pin=self.nand2.get_pin("vdd")
# Create the enable pin that connects all write mask AND array's B pins
beg_en_pin = self.and2_insts[0].get_pin("B")
@ -114,16 +112,16 @@ class write_mask_and_array(design.design):
# Extend metal3 to edge of AND array in multiport
en_to_edge = self.and2.width - beg_en_pin.cx()
self.add_layout_pin(text="en",
layer="metal3",
layer="m3",
offset=beg_en_pin.bc(),
width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge)
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
else:
self.add_layout_pin(text="en",
layer="metal3",
layer="m3",
offset=beg_en_pin.bc(),
width=end_en_pin.cx() - beg_en_pin.cx())
@ -134,20 +132,21 @@ class write_mask_and_array(design.design):
# Add via connections to metal3 for AND array's B pin
en_pin = self.and2_insts[i].get_pin("B")
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=en_pin.center())
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=en_pin.center())
self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0))
self.add_power_pin("vdd", vector(supply_pin.width() + i * self.wmask_en_len, self.height))
# Route power and ground rails together
if i < self.num_wmasks-1:
for n in ["gnd","vdd"]:
pin = self.and2_insts[i].get_pin(n)
next_pin = self.and2_insts[i+1].get_pin(n)
self.add_path("metal1",[pin.center(),next_pin.center()])
for supply in ["gnd", "vdd"]:
supply_pin=self.and2_insts[i].get_pin(supply)
self.add_power_pin(supply, supply_pin.center())
for supply in ["gnd", "vdd"]:
supply_pin_left = self.and2_insts[0].get_pin(supply)
supply_pin_right = self.and2_insts[self.num_wmasks-1].get_pin(supply)
self.add_path("m1",[supply_pin_left.lc(), supply_pin_right.rc()])
def get_cin(self):
"""Get the relative capacitance of all the input connections in the bank"""
# The enable is connected to an and2 for every row.

View File

@ -76,14 +76,14 @@ class pand2(pgate.pgate):
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("metal1",
self.add_path("m1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -91,7 +91,7 @@ class pand2(pgate.pgate):
# Continous gnd rail along with label.
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())

View File

@ -76,14 +76,14 @@ class pand3(pgate.pgate):
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("metal1",
self.add_path("m1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -91,7 +91,7 @@ class pand3(pgate.pgate):
# Continous gnd rail along with label.
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())

View File

@ -78,13 +78,13 @@ class pbuf(pgate.pgate):
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -92,7 +92,7 @@ class pbuf(pgate.pgate):
# Continous gnd rail along with label.
gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())

View File

@ -140,7 +140,7 @@ class pdriver(pgate.pgate):
z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A"))
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
self.add_path("metal1",
self.add_path("m1",
[z_inst_list[x].center(), mid_point,
a_inst_list[x].center()])
@ -148,7 +148,7 @@ class pdriver(pgate.pgate):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst_list[0].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -156,7 +156,7 @@ class pdriver(pgate.pgate):
# Continous gnd rail along with label.
gnd_pin = self.inv_inst_list[0].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())

View File

@ -8,10 +8,9 @@
import contact
import design
import debug
from tech import layer, drc
from tech import layer
from vector import vector
from globals import OPTS
from sram_factory import factory
class pgate(design.design):
@ -27,9 +26,9 @@ class pgate(design.design):
if height:
self.height = height
elif not height:
b = factory.create(module_type="bitcell")
self.height = b.height
# By default, we make it 8 M1 pitch tall
self.height = 8*self.m1_pitch
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -59,12 +58,12 @@ class pgate(design.design):
debug.error("Invalid supply name.", -1)
if abs(height) > 0:
self.add_rect(layer="metal1",
self.add_rect(layer="m1",
offset=source_pin.ll(),
height=height,
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False):
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"):
"""
Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or
@ -75,11 +74,13 @@ class pgate(design.design):
pmos_gate_pin = pmos_inst.get_pin("G")
# Check if the gates are aligned and give an error if they aren't!
if nmos_gate_pin.ll().x != pmos_gate_pin.ll().x:
self.gds_write("unaliged_gates.gds")
debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x,
"Connecting unaligned gates not supported.")
"Connecting unaligned gates not supported. See unaligned_gates.gds.")
# Pick point on the left of NMOS and connect down to PMOS
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0)
# Pick point on the left of NMOS and up to PMOS
nmos_gate_pos = nmos_gate_pin.ul() + vector(0.5 * self.poly_width, 0)
pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y)
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
@ -87,23 +88,16 @@ class pgate(design.design):
left_gate_offset = vector(nmos_gate_pin.lx(), ypos)
# Center is completely symmetric.
if rotate:
contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width
directions = ("H", "V")
else:
contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height
directions = ("V", "H")
contact_width = contact.poly_contact.width
contact_m1_width = contact.poly_contact.second_layer_width
contact_m1_height = contact.poly_contact.second_layer_height
if position == "center":
contact_offset = left_gate_offset \
+ vector(0.5 * self.poly_width, 0)
elif position == "farleft":
contact_offset = left_gate_offset \
- vector(0.5 * contact.poly.width, 0)
- vector(0.5 * contact.poly_contact.width, 0)
elif position == "left":
contact_offset = left_gate_offset \
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
@ -113,83 +107,87 @@ class pgate(design.design):
else:
debug.error("Invalid contact placement option.", -1)
# Non-preferred direction via
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=contact_offset,
directions=directions)
v=self.add_via_center(layers=self.poly_stack,
offset=contact_offset)
self.add_layout_pin_rect_center(text=name,
layer="metal1",
layer="m1",
offset=contact_offset,
width=contact_m1_width,
height=contact_m1_height)
# This is to ensure that the contact is
# connected to the gate
mid_point = contact_offset.scale(0.5, 1) \
+ left_gate_offset.scale(0.5, 0)
self.add_rect_center(layer="poly",
offset=mid_point,
height=contact.poly.first_layer_width,
height=contact.poly_contact.first_layer_width,
width=left_gate_offset.x - contact_offset.x)
def extend_wells(self, middle_position):
def extend_wells(self):
""" Extend the n/p wells to cover whole cell """
# Add a rail width to extend the well to the top of the rail
max_y_offset = self.height + 0.5 * self.m1_width
self.nwell_position = middle_position
nwell_height = max_y_offset - middle_position.y
if layer["nwell"]:
# This should match the cells in the cell library
nwell_y_offset = 0.48 * self.height
full_height = self.height + 0.5*self.m1_width
# FIXME: float rounding problem
if "nwell" in layer:
# Add a rail width to extend the well to the top of the rail
nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
full_height)
nwell_position = vector(0, nwell_y_offset) - vector(self.well_extend_active, 0)
nwell_height = nwell_max_offset - nwell_y_offset
self.add_rect(layer="nwell",
offset=middle_position,
width=self.well_width,
height=nwell_height)
if layer["vtg"]:
self.add_rect(layer="vtg",
offset=self.nwell_position,
offset=nwell_position,
width=self.well_width,
height=nwell_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=nwell_position,
width=self.well_width,
height=nwell_height)
pwell_position = vector(0, -0.5 * self.m1_width)
pwell_height = middle_position.y - pwell_position.y
if layer["pwell"]:
# Start this half a rail width below the cell
if "pwell" in layer:
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
-0.5 * self.m1_width)
pwell_position = vector(-self.well_extend_active, pwell_min_offset)
pwell_height = nwell_y_offset - pwell_position.y
self.add_rect(layer="pwell",
offset=pwell_position,
width=self.well_width,
height=pwell_height)
if layer["vtg"]:
self.add_rect(layer="vtg",
offset=pwell_position,
width=self.well_width,
height=pwell_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=pwell_position,
width=self.well_width,
height=pwell_height)
def add_nwell_contact(self, pmos, pmos_pos):
""" Add an nwell contact next to the given pmos device. """
layer_stack = ("active", "contact", "metal1")
layer_stack = self.active_stack
# To the right a spacing away from the pmos right active edge
contact_xoffset = pmos_pos.x + pmos.active_width \
+ drc("active_to_body_active")
+ self.active_space
# Must be at least an well enclosure of active down
# from the top of the well
# OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5 * self.m1_width
contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.well_enclose_active)
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.nwell_enclose_active)
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
0.5 * pmos.active_contact.first_layer_height)
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H", "V"),
implant_type="n",
well_type="n")
self.add_rect_center(layer="metal1",
self.add_rect_center(layer="m1",
offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y)
@ -221,17 +219,17 @@ class pgate(design.design):
def add_pwell_contact(self, nmos, nmos_pos):
""" Add an pwell contact next to the given nmos device. """
layer_stack = ("active", "contact", "metal1")
layer_stack = self.active_stack
pwell_position = vector(0, -0.5 * self.m1_width)
# To the right a spacing away from the nmos right active edge
contact_xoffset = nmos_pos.x + nmos.active_width \
+ drc("active_to_body_active")
+ self.active_space
# Must be at least an well enclosure of active up
# from the bottom of the well
contact_yoffset = max(nmos_pos.y,
self.well_enclose_active \
self.nwell_enclose_active \
- nmos.active_contact.first_layer_height / 2)
contact_offset = vector(contact_xoffset, contact_yoffset)
@ -240,10 +238,9 @@ class pgate(design.design):
0.5 * nmos.active_contact.first_layer_height)
self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H", "V"),
implant_type="p",
well_type="p")
self.add_rect_center(layer="metal1",
self.add_rect_center(layer="m1",
offset=contact_offset.scale(1,0.5),
width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y)

View File

@ -52,10 +52,10 @@ class pinv(pgate.pgate):
def create_layout(self):
""" Calls all functions related to the generation of the layout """
self.setup_layout_constants()
self.route_supply_rails()
self.place_ptx()
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.extend_wells()
self.route_supply_rails()
self.connect_rails()
self.route_input_gate(self.pmos_inst,
self.nmos_inst,
@ -89,22 +89,24 @@ class pinv(pgate.pgate):
# Assume we need 3 metal 1 pitches (2 power rails, one
# between the tx for the drain)
# plus the tx height
nmos = factory.create(module_type="ptx", tx_type="nmos")
nmos = factory.create(module_type="ptx",
tx_type="nmos")
pmos = factory.create(module_type="ptx",
width=drc("minwidth_tx"),
tx_type="pmos")
tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing
min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2 * drc("poly_to_active"))
min_channel = max(contact.poly_contact.width + self.m1_space,
contact.poly_contact.width + 2 * self.poly_to_active)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
self.poly_extend_active + self.poly_space)
total_height = tx_height + min_channel + 2 * self.top_bottom_space
debug.check(self.height > total_height,
"Cell height {0} too small for simple min height {1}.".format(self.height,
total_height))
@ -115,8 +117,8 @@ class pinv(pgate.pgate):
# Divide the height in half. Could divide proportional to beta,
# but this makes connecting wells of multiple cells easier.
# Subtract the poly space under the rail of the tx
nmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
pmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
nmos_height_available = 0.5 * tx_height_available - 0.5 * self.poly_space
pmos_height_available = 0.5 * tx_height_available - 0.5 * self.poly_space
debug.info(2,
"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
@ -147,14 +149,18 @@ class pinv(pgate.pgate):
def setup_layout_constants(self):
"""
Pre-compute some handy layout parameters.
Compute the width and height
"""
# the well width is determined the multi-finger PMOS device width plus
# the well contact width and half well enclosure on both sides
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.width = self.well_width
# the width is determined the multi-finger PMOS device width plus
# the well contact width, spacing between them
# space is for power supply contact to nwell m1 spacing
self.width = self.pmos.active_offset.x + self.pmos.active_width \
+ self.active_space + contact.nwell_contact.width \
+ 0.5 * self.nwell_enclose_active \
+ self.m1_space
# This includes full enclosures on each end
self.well_width = self.width + 2*self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
def add_ptx(self):
@ -178,12 +184,12 @@ class pinv(pgate.pgate):
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
@ -222,9 +228,6 @@ class pinv(pgate.pgate):
nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y))
# This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy())
def route_outputs(self):
"""
Route the output (drains) together.
@ -238,7 +241,7 @@ class pinv(pgate.pgate):
# Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos])
self.add_path("m1", [nmos_drain_pos, pmos_drain_pos])
# Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
@ -247,13 +250,13 @@ class pinv(pgate.pgate):
# This extends the output to the edge of the cell
output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0)
self.add_layout_pin_segment_center(text="Z",
layer="metal1",
layer="m1",
start=mid_drain_offset,
end=output_offset)
else:
# This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
layer="m1",
offset=mid_drain_offset \
+ vector(0.5 * self.m1_width, 0))

View File

@ -114,21 +114,21 @@ class pinvbuf(pgate.pgate):
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()])
# inv2 Z to inv3 A
z2_pin = self.inv2_inst.get_pin("Z")
a3_pin = self.inv3_inst.get_pin("A")
mid_point = vector(z2_pin.cx(), a3_pin.cy())
self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()])
self.add_path("m1", [z2_pin.center(), mid_point, a3_pin.center()])
# inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(("metal1", "via1", "metal2"),
self.add_wire(self.m1_stack,
[z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=z1_pin.center())
def add_layout_pins(self):
@ -136,7 +136,7 @@ class pinvbuf(pgate.pgate):
# Continous vdd rail along with label.
vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -144,7 +144,7 @@ class pinvbuf(pgate.pgate):
# Continous vdd rail along with label.
gnd_pin = self.inv4_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=gnd_pin.height())
@ -152,30 +152,30 @@ class pinvbuf(pgate.pgate):
# Continous gnd rail along with label.
gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv4_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer="metal2",
layer="m2",
offset=z_pin.center())
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=z_pin.center())
zb_pin = self.inv3_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Zb",
layer="metal2",
layer="m2",
offset=zb_pin.center())
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer="metal2",
layer="m2",
offset=a_pin.center())
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=a_pin.center())
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -53,7 +53,7 @@ class pnand2(pgate.pgate):
self.place_ptx()
self.connect_rails()
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.extend_wells()
self.route_inputs()
self.route_output()
@ -85,10 +85,10 @@ class pnand2(pgate.pgate):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly.first_layer_width,
self.m1_space + contact.m1m2.first_layer_width,
self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width)
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location,
@ -98,11 +98,11 @@ class pnand2(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + contact.active.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = 2 * self.pmos.active_width + contact.active_contact.width \
+ 2 * self.active_space \
+ 0.5 * self.nwell_enclose_active
self.width = self.well_width
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules
@ -110,17 +110,17 @@ class pnand2(pgate.pgate):
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
layer="m1",
offset=vector(0.5*self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
@ -170,9 +170,6 @@ class pnand2(pgate.pgate):
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
"""
Add n/p well taps to the layout and connect to supplies
@ -224,27 +221,29 @@ class pnand2(pgate.pgate):
# Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y)
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center(),
directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(),
directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
# Non-preferred active contacts
self.add_via_center(layers=self.m1_stack,
directions=("V", "H"),
offset=pmos_pin.center())
# Non-preferred active contacts
self.add_via_center(layers=self.m1_stack,
directions=("V", "H"),
offset=nmos_pin.center())
self.add_via_center(layers=self.m1_stack,
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",
self.add_path("m2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
layer="m1",
offset=out_offset,
width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width)
width=contact.m1_via.first_layer_height,
height=contact.m1_via.first_layer_width)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""

View File

@ -61,7 +61,7 @@ class pnand3(pgate.pgate):
self.place_ptx()
self.connect_rails()
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.extend_wells()
self.route_inputs()
self.route_output()
@ -91,34 +91,29 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \
self.width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2 * self.active_space + 0.5 * self.nwell_enclose_active \
- self.overlap_offset.x
self.width = self.well_width
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
# This will help with the wells and the input/output placement
self.output_pos = vector(0, 0.5*self.height)
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \
+ extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
@ -178,10 +173,10 @@ class pnand3(pgate.pgate):
self.nmos3_pos = nmos2_pos + self.overlap_offset
self.nmos3_inst.place(self.nmos3_pos)
# This should be placed at the top of the NMOS well
self.well_pos = vector(0, self.nmos1_inst.uy())
# This will help with the wells and the input/output placement
self.output_pos = vector(0, 0.5*self.height)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -202,10 +197,10 @@ class pnand3(pgate.pgate):
# wire space or wire and one contact space
metal_spacing = max(self.m1_space + self.m1_width,
self.m2_space + self.m2_width,
self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width)
self.m1_space + 0.5 *contact.poly_contact.width + 0.5 * self.m1_width)
active_spacing = max(self.m1_space,
0.5 * contact.poly.first_layer_width + drc("poly_to_active"))
0.5 * contact.poly_contact.first_layer_width + self.poly_to_active)
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
@ -237,27 +232,30 @@ class pnand3(pgate.pgate):
nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos1_pin.center())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos3_pin.center())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos3_pin.center())
self.add_via_center(layers=self.m1_stack,
offset=pmos1_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=pmos3_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack,
offset=nmos3_pin.center(),
directions=("V", "V"))
# PMOS3 and NMOS3 are drain aligned
self.add_path("metal2", [pmos3_pin.bc(), nmos3_pin.uc()])
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.uc()])
# Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=mid_offset)
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
layer="m1",
offset=mid_offset,
width=contact.m1m2.first_layer_width,
height=contact.m1m2.first_layer_height)
width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""

View File

@ -52,7 +52,7 @@ class pnor2(pgate.pgate):
self.place_ptx()
self.connect_rails()
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.extend_wells()
self.route_inputs()
self.route_output()
@ -84,10 +84,10 @@ class pnor2(pgate.pgate):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly.first_layer_width,
self.m1_space + contact.m1m2.first_layer_width,
self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width)
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins
@ -95,12 +95,11 @@ class pnor2(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width \
self.width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
+ 2 * self.active_space \
+ 0.5 * self.nwell_enclose_active
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules
@ -108,18 +107,18 @@ class pnor2(pgate.pgate):
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"),
self.poly_extend_active,
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
@ -137,7 +136,6 @@ class pnor2(pgate.pgate):
mod=self.pmos)
self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
@ -170,9 +168,6 @@ class pnor2(pgate.pgate):
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -216,9 +211,9 @@ class pnor2(pgate.pgate):
nmos2_pin = self.nmos2_inst.get_pin("D")
# Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=pmos_pin.center())
m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"),
m1m2_contact = self.add_via_center(layers=self.m1_stack,
offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
@ -226,18 +221,18 @@ class pnor2(pgate.pgate):
mid3_offset = mid2_offset + vector(self.m2_width, 0)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",
[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",
self.add_path("m2",
[pmos_pin.center(), mid2_offset, mid3_offset])
self.add_path("m2",
[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=mid3_offset)
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
layer="m1",
offset=mid3_offset,
width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width)
width=contact.m1_via.first_layer_height,
height=contact.m1_via.first_layer_width)
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""

View File

@ -8,7 +8,7 @@
import contact
import design
import debug
from tech import drc, parameter
from tech import parameter
from vector import vector
from globals import OPTS
from sram_factory import factory
@ -38,6 +38,12 @@ class precharge(design.design):
self.create_layout()
self.DRC_LVS()
def get_bl_names(self):
return "bl"
def get_br_names(self):
return "br"
def create_netlist(self):
self.add_pins()
self.add_ptx()
@ -72,7 +78,7 @@ class precharge(design.design):
# Adds the rail across the width of the cell
vdd_position = vector(0.5 * self.width, self.height)
self.add_rect_center(layer="metal1",
self.add_rect_center(layer="m1",
offset=vdd_position,
width=self.width,
height=self.m1_width)
@ -80,7 +86,7 @@ class precharge(design.design):
pmos_pin = self.upper_pmos2_inst.get_pin("S")
# center of vdd rail
pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos])
self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos])
# Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
@ -116,12 +122,12 @@ class precharge(design.design):
# adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.well_enclose_active),
self.nwell_enclose_active),
self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout
ydiff = self.pmos.height + 2 * self.m1_space + contact.poly.width
ydiff = self.pmos.height + 2 * self.m1_space + contact.poly_contact.width
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
@ -159,12 +165,12 @@ class precharge(design.design):
# adds the en contact to connect the gates to the en rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() \
+ vector(0, 0.5 * self.poly_space)
self.add_via_center(layers=("poly", "contact", "metal1"),
self.add_via_center(layers=self.poly_stack,
offset=offset)
# adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar",
layer="metal1",
layer="m1",
start=offset.scale(0, 1),
end=offset.scale(0, 1) + vector(self.width, 0))
@ -175,15 +181,15 @@ class precharge(design.design):
# adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \
+ drc("well_extend_active"))
self.add_via_center(layers=("active", "contact", "metal1"),
+ vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \
+ self.nwell_extend_active)
self.add_via_center(layers=self.active_stack,
offset=well_contact_pos,
implant_type="n",
well_type="n")
# leave an extra pitch for the height
self.height = well_contact_pos.y + contact.well.height + self.m1_pitch
self.height = well_contact_pos.y + contact.active_contact.height + self.m1_pitch
# nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell",
@ -200,18 +206,16 @@ class precharge(design.design):
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2",
layer="m2",
offset=offset,
width=drc("minwidth_metal2"),
height=self.height)
# adds the BR on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.br_pin = self.add_layout_pin(text="br",
layer="metal2",
layer="m2",
offset=offset,
width=drc("minwidth_metal2"),
height=self.height)
def connect_to_bitlines(self):
@ -233,23 +237,22 @@ class precharge(design.design):
Adds contacts/via from metal1 to metal2 for bit-lines
"""
stack = ("metal1", "via1", "metal2")
upper_pin = self.upper_pmos1_inst.get_pin("S")
lower_pin = self.lower_pmos_inst.get_pin("S")
# BL goes up to M2 at the transistor
self.bl_contact =self.add_via_center(layers=stack,
self.bl_contact =self.add_via_center(layers=self.m1_stack,
offset=upper_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=stack,
self.add_via_center(layers=self.m1_stack,
offset=lower_pin.center(),
directions=("V", "V"))
# BR routes over on M1 first
self.add_via_center(layers=stack,
self.add_via_center(layers=self.m1_stack,
offset=vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V", "V"))
self.add_via_center(layers=stack,
self.add_via_center(layers=self.m1_stack,
offset=vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V", "V"))
@ -261,7 +264,7 @@ class precharge(design.design):
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
self.add_path("metal1", [left_pos, right_pos] )
self.add_path("m1", [left_pos, right_pos] )
def connect_pmos_m2(self, pmos_pin, bit_pin):
"""
@ -271,7 +274,7 @@ class precharge(design.design):
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
self.add_path("metal2", [left_pos, right_pos], self.bl_contact.height)
self.add_path("m2", [left_pos, right_pos], self.bl_contact.height)
def get_en_cin(self):
"""Get the relative capacitance of the enable in the precharge cell"""

View File

@ -44,7 +44,7 @@ class ptristate_inv(pgate.pgate):
""" Calls all functions related to the generation of the netlist """
self.add_pins()
self.add_ptx()
self.create_ptx()
self.create_ptx()
def create_layout(self):
""" Calls all functions related to the generation of the layout """
@ -61,7 +61,6 @@ class ptristate_inv(pgate.pgate):
""" Adds pins for spice netlist """
self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"])
def setup_layout_constants(self):
"""
Pre-compute some handy layout parameters.
@ -73,15 +72,14 @@ class ptristate_inv(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width + self.nwell_enclose_active
# Add an extra space because we route the output on the right of the S/D
self.width = self.well_width + 0.5 * self.m1_space
# Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below
self.top_bottom_space = max(contact.well.width, contact.well.height)
self.top_bottom_space = max(contact.active_contact.width, contact.active_contact.height)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
@ -95,18 +93,17 @@ class ptristate_inv(pgate.pgate):
width=self.pmos_width,
mults=1,
tx_type="pmos")
self.add_mod(self.pmos)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
@ -138,8 +135,8 @@ class ptristate_inv(pgate.pgate):
"""
pmos_yoff = self.height - self.pmos.active_height \
- self.top_bottom_space - 0.5 * contact.well.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height
- self.top_bottom_space - 0.5 * contact.active_contact.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.active_contact.height
# Tristate transistors
pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff)
@ -181,7 +178,7 @@ class ptristate_inv(pgate.pgate):
pmos_drain_pos = pmos_drain_pin.ur()
self.add_layout_pin(text="out",
layer="metal1",
layer="m1",
offset=nmos_drain_pos,
height=pmos_drain_pos.y - nmos_drain_pos.y)
@ -191,7 +188,7 @@ class ptristate_inv(pgate.pgate):
supplies AFTER the wells are created
"""
layer_stack = ("active", "contact", "metal1")
layer_stack = self.active_stack
drain_pos = self.nmos1_inst.get_pin("S").center()
vdd_pos = self.get_pin("vdd").center()

View File

@ -52,18 +52,17 @@ class ptx(design.design):
self.connect_poly = connect_poly
self.num_contacts = num_contacts
# Do NOT create the netlist and layout (not a pgate)
# Since it has variable height, it is not a pgate.
self.create_netlist()
# We must always create ptx layout for pbitcell
# some transistor sizes in other netlist depend on pbitcell
self.create_layout()
#ll = self.find_lowest_coords()
#ur = self.find_highest_coords()
#self.add_boundary(ll, ur)
ll = self.find_lowest_coords()
ur = self.find_highest_coords()
self.add_boundary(ll, ur)
# (0,0) will be the corner ofthe active area (not the larger well)
# (0,0) will be the corner of the active area (not the larger well)
self.translate_all(self.active_offset)
def create_layout(self):
@ -92,15 +91,15 @@ class ptx(design.design):
# " ".join(self.pins)))
# Just make a guess since these will actually
# be decided in the layout later.
area_sd = 2.5 * drc("minwidth_poly") * self.tx_width
perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width
area_sd = 2.5 * self.poly_width * self.tx_width
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device= main_str + area_str
self.spice_device = main_str + area_str
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))
@ -109,8 +108,8 @@ class ptx(design.design):
Pre-compute some handy layout parameters.
"""
if self.num_contacts==None:
self.num_contacts=self.calculate_num_contacts()
if not self.num_contacts:
self.num_contacts = self.calculate_num_contacts()
# Determine layer types needed
if self.tx_type == "nmos":
@ -120,72 +119,83 @@ class ptx(design.design):
self.implant_type = "p"
self.well_type = "n"
else:
self.error("Invalid transitor type.",-1)
self.error("Invalid transitor type.", -1)
# This is not actually instantiated but used for calculations
self.active_contact = factory.create(module_type="contact",
layer_stack=("active", "contact", "metal1"),
layer_stack=self.active_stack,
directions=("V", "V"),
dimensions=(1, self.num_contacts))
# The contacted poly pitch (or uncontacted in an odd technology)
self.poly_pitch = max(2 * self.active_contact_to_gate + self.contact_width + self.poly_width,
# The contacted poly pitch
self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
self.poly_space)
# The contacted poly pitch (or uncontacted in an odd technology)
self.contact_pitch = 2 * self.active_contact_to_gate + self.contact_width + self.poly_width
# The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(drc("active_enclosure_active_contact"),
(self.active_width - self.contact_width) / 2)
# This is the distance from the edge of poly to the contacted end of active
self.end_to_poly = active_enclose_contact + self.contact_width + self.active_contact_to_gate
# The contacted poly pitch
self.contact_spacing = 2 * self.contact_to_gate + \
self.contact_width + self.poly_width
# This is measured because of asymmetric enclosure rules
active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width)
# This is the distance from the side of
# poly gate to the contacted end of active
# (i.e. the "outside" contacted diffusion sizes)
self.end_to_poly = self.active_contact.width - active_enclose_contact + \
self.contact_to_gate
# Active width is determined by enclosure on both ends and contacted pitch,
# at least one poly and n-1 poly pitches
self.active_width = 2 * self.end_to_poly + self.poly_width + (self.mults - 1) * self.poly_pitch
self.active_width = 2 * self.end_to_poly + self.poly_width + \
(self.mults - 1) * self.poly_pitch
# Active height is just the transistor width
self.active_height = self.tx_width
# Poly height must include poly extension over active
self.poly_height = self.tx_width + 2 * self.poly_extend_active
# The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active] * 2)
if "pwell" in layer:
pwell_enclose_active = drc("pwell_enclose_active")
else:
pwell_enclose_active = 0
if "nwell" in layer:
nwell_enclose_active = drc("nwell_enclose_active")
else:
nwell_enclose_active = 0
# Use the max of either so that the poly gates will align properly
well_enclose_active = max(pwell_enclose_active,
nwell_enclose_active)
self.active_offset = vector([well_enclose_active] * 2)
# Well enclosure of active, ensure minwidth as well
well_name = "{}well".format(self.well_type)
if layer[well_name]:
self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active,
self.well_width)
self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active,
self.well_width)
if well_name in layer:
well_width_rule = drc("minwidth_" + well_name)
well_enclose_active = drc(well_name + "_enclose_active")
self.well_width = max(self.active_width + 2 * well_enclose_active,
well_width_rule)
self.well_height = max(self.active_height + 2 * well_enclose_active,
well_width_rule)
# We are going to shift the 0,0, so include that in the width and height
self.height = self.cell_well_height - self.active_offset.y
self.width = self.cell_well_width - self.active_offset.x
self.height = self.well_height - self.active_offset.y
self.width = self.well_width - self.active_offset.x
else:
# If no well, use the boundary of the active and poly
# The well is not included in the height and width
self.height = self.poly_height
self.width = self.active_width
# The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active] * 2)
# This is the center of the first active contact offset (centered vertically)
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width,
self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width,
0.5 * self.active_height)
# Min area results are just flagged for now.
debug.check(self.active_width * self.active_height >= drc("minarea_active"),
debug.check(self.active_width * self.active_height >= self.minarea_active,
"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix
# an area problem as it would cause an LVS issue.
debug.check(self.poly_width * self.poly_height >= drc("minarea_poly"),
debug.check(self.poly_width * self.poly_height >= self.minarea_poly,
"Minimum poly area violated.")
def connect_fingered_poly(self, poly_positions):
@ -215,13 +225,13 @@ class ptx(design.design):
poly_offset = poly_positions[0] + vector(-0.5 * self.poly_width,
distance_above_active)
# Remove the old pin and add the new one
self.remove_layout_pin("G") # only keep the main pin
# only keep the main pin
self.remove_layout_pin("G")
self.add_layout_pin(text="G",
layer="poly",
offset=poly_offset,
width=poly_width,
height=drc("minwidth_poly"))
height=self.poly_width)
def connect_fingered_active(self, drain_positions, source_positions):
"""
@ -230,10 +240,10 @@ class ptx(design.design):
# This is the distance that we must route up or down from the center
# of the contacts to avoid DRC violations to the other contacts
pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \
+ self.m1_space + 0.5 * self.m1_width)
pin_offset = vector(0,
0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width)
# This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width/2,0)
end_offset = vector(self.m1_width / 2.0, 0)
# drains always go to the MIDDLE of the cell,
# so top of NMOS, bottom of PMOS
@ -246,16 +256,17 @@ class ptx(design.design):
source_dir = -1
if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir,source_dir)
self.remove_layout_pin("S") # remove the individual connections
source_offset = pin_offset.scale(source_dir, source_dir)
# remove the individual connections
self.remove_layout_pin("S")
# Add each vertical segment
for a in source_positions:
self.add_path(("metal1"),
self.add_path(("m1"),
[a, a + pin_offset.scale(source_dir,
source_dir)])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="S",
layer="metal1",
layer="m1",
start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1] + source_offset + end_offset)
@ -264,10 +275,10 @@ class ptx(design.design):
self.remove_layout_pin("D") # remove the individual connections
# Add each vertical segment
for a in drain_positions:
self.add_path(("metal1"), [a,a+drain_offset])
self.add_path(("m1"), [a,a+drain_offset])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="D",
layer="metal1",
layer="m1",
start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1] + drain_offset + end_offset)
@ -314,7 +325,7 @@ class ptx(design.design):
height=self.active_height)
# If the implant must enclose the active, shift offset
# and increase width/height
enclose_width = drc("implant_enclosure_active")
enclose_width = self.implant_enclose_active
enclose_offset = [enclose_width] * 2
self.add_rect(layer="{}implant".format(self.implant_type),
offset=self.active_offset - enclose_offset,
@ -326,26 +337,31 @@ class ptx(design.design):
Add an (optional) well and implant for the type of transistor.
"""
well_name = "{}well".format(self.well_type)
if layer[well_name]:
self.add_rect(layer=well_name,
offset=(0,0),
width=self.cell_well_width,
height=self.cell_well_height)
if layer["vtg"]:
self.add_rect(layer="vtg",
offset=(0,0),
width=self.cell_well_width,
height=self.cell_well_height)
if not (well_name in layer or "vtg" in layer):
return
center_pos = self.active_offset + vector(0.5*self.active_width,
0.5*self.active_height)
well_ll = center_pos - vector(0.5*self.well_width,
0.5*self.well_height)
if well_name in layer:
self.add_rect(layer=well_name,
offset=well_ll,
width=self.well_width,
height=self.well_height)
if "vtg" in layer:
self.add_rect(layer="vtg",
offset=well_ll,
width=self.well_width,
height=self.well_height)
def calculate_num_contacts(self):
"""
"""
Calculates the possible number of source/drain contacts in a finger.
For now, it is hard set as 1.
"""
return 1
def get_contact_positions(self):
"""
Create a list of the centers of drain and source contact positions.
@ -359,10 +375,10 @@ class ptx(design.design):
for i in range(self.mults):
if i%2:
# It's a source... so offset from previous drain.
source_positions.append(drain_positions[-1] + vector(self.contact_pitch, 0))
source_positions.append(drain_positions[-1] + vector(self.contact_spacing, 0))
else:
# It's a drain... so offset from previous source.
drain_positions.append(source_positions[-1] + vector(self.contact_pitch, 0))
drain_positions.append(source_positions[-1] + vector(self.contact_spacing, 0))
return [source_positions,drain_positions]
@ -374,28 +390,28 @@ class ptx(design.design):
[source_positions,drain_positions] = self.get_contact_positions()
for pos in source_positions:
contact=self.add_via_center(layers=("active", "contact", "metal1"),
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("H","V"),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="S",
layer="metal1",
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
for pos in drain_positions:
contact=self.add_via_center(layers=("active", "contact", "metal1"),
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("H","V"),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="D",
layer="metal1",
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)

View File

@ -6,14 +6,13 @@
#All rights reserved.
#
import design
from tech import drc, parameter, spice
from tech import parameter
import debug
import math
from tech import drc
from vector import vector
from globals import OPTS
from sram_factory import factory
class pwrite_driver(design.design):
"""
The pwrite_driver is two tristate inverters that drive the bitlines.
@ -40,12 +39,10 @@ class pwrite_driver(design.design):
# Creates the netlist and layout
# Since it has variable height, it is not a pgate.
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
self.DRC_LVS()
def create_netlist(self):
self.add_pins()
self.add_modules()
@ -55,8 +52,6 @@ class pwrite_driver(design.design):
self.place_modules()
self.route_wires()
self.route_supplies()
def add_pins(self):
self.add_pin("din", "INPUT")
@ -66,17 +61,19 @@ class pwrite_driver(design.design):
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
# Tristate inverter
self.tri = factory.create(module_type="ptristate_inv", height="min")
self.add_mod(self.tri)
debug.check(self.tri.width<self.width,"Could not create tristate inverter to match bitcell width")
debug.check(self.tri.width<self.width,
"Could not create tristate inverter to match bitcell width")
#self.tbuf = factory.create(module_type="ptristate_buf", height="min")
#self.tbuf = factory.create(module_type="ptristate_buf",
#height="min")
#self.add_mod(self.tbuf)
#debug.check(self.tbuf.width<self.width,"Could not create tristate buffer to match bitcell width")
#debug.check(self.tbuf.width<self.width,
#"Could not create tristate buffer to match bitcell width")
# Inverter for din and en
self.inv = factory.create(module_type="pinv", under_rail_vias=True)
@ -131,26 +128,26 @@ class pwrite_driver(design.design):
bl_xoffset = left_x
bl_out=vector(bl_xoffset, self.height)
bl_in=self.bl_inst.get_pin("out").center()
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=bl_in)
bl_mid = vector(bl_out.x,bl_in.y)
self.add_path("metal2", [bl_in, bl_mid, bl_out])
self.add_path("m2", [bl_in, bl_mid, bl_out])
self.add_layout_pin_rect_center(text="bl",
layer="metal2",
layer="m2",
offset=bl_out)
br_xoffset = right_x
br_out=vector(br_xoffset, self.height)
br_in=self.br_inst.get_pin("out").center()
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=self.m1_stack,
offset=br_in)
br_mid = vector(br_out.x,br_in.y)
self.add_path("metal2", [br_in, br_mid, br_out])
self.add_path("m2", [br_in, br_mid, br_out])
self.add_layout_pin_rect_center(text="br",
layer="metal2",
layer="m2",
offset=br_out)
#br_xoffset = b.get_pin("br".cx()
@ -162,19 +159,19 @@ class pwrite_driver(design.design):
track_xoff = self.get_m2_track(1)
din_loc = self.din_inst.get_pin("A").center()
self.add_via_stack("metal1", "metal2", din_loc)
self.add_via_stack("m1", "m2", din_loc)
din_track = vector(track_xoff,din_loc.y)
br_in = self.br_inst.get_pin("in").center()
self.add_via_stack("metal1", "metal2", br_in)
self.add_via_stack("m1", "m2", br_in)
br_track = vector(track_xoff,br_in.y)
din_in = vector(track_xoff,0)
self.add_path("metal2", [din_in, din_track, din_loc, din_track, br_track, br_in])
self.add_path("m2", [din_in, din_track, din_loc, din_track, br_track, br_in])
self.add_layout_pin_rect_center(text="din",
layer="metal2",
layer="m2",
offset=din_in)
def route_din_bar(self):
@ -183,19 +180,19 @@ class pwrite_driver(design.design):
track_xoff = self.get_m4_track(self.din_bar_track)
din_bar_in = self.din_inst.get_pin("Z").center()
self.add_via_stack("metal1", "metal3", din_bar_in)
self.add_via_stack("m1", "m3", din_bar_in)
din_bar_track = vector(track_xoff,din_bar_in.y)
bl_in = self.bl_inst.get_pin("in").center()
self.add_via_stack("metal1", "metal3", bl_in)
self.add_via_stack("m1", "m3", bl_in)
bl_track = vector(track_xoff,bl_in.y)
din_in = vector(track_xoff,0)
self.add_wire(("metal3","via3","metal4"), [din_bar_in, din_bar_track, bl_track, bl_in])
self.add_wire(self.m3_stack, [din_bar_in, din_bar_track, bl_track, bl_in])
self.add_layout_pin_rect_center(text="din",
layer="metal4",
layer="m4",
offset=din_in)
@ -206,22 +203,22 @@ class pwrite_driver(design.design):
# This M2 pitch is a hack since the A and Z pins align horizontally
en_bar_loc = self.en_inst.get_pin("Z").uc()
en_bar_track = vector(track_xoff, en_bar_loc.y)
self.add_via_stack("metal1", "metal3", en_bar_loc)
self.add_via_stack("m1", "m3", en_bar_loc)
# This is a U route to the right down then left
bl_en_loc = self.bl_inst.get_pin("en_bar").center()
bl_en_track = vector(track_xoff, bl_en_loc.y)
self.add_via_stack("metal1", "metal3", bl_en_loc)
self.add_via_stack("m1", "m3", bl_en_loc)
br_en_loc = self.br_inst.get_pin("en_bar").center()
br_en_track = vector(track_xoff, bl_en_loc.y)
self.add_via_stack("metal1", "metal3", br_en_loc)
self.add_via_stack("m1", "m3", br_en_loc)
# L shape
self.add_wire(("metal3","via3","metal4"),
self.add_wire(self.m3_stack,
[en_bar_loc, en_bar_track, bl_en_track])
# U shape
self.add_wire(("metal3","via3","metal4"),
self.add_wire(self.m3_stack,
[bl_en_loc, bl_en_track, br_en_track, br_en_loc])
@ -233,30 +230,30 @@ class pwrite_driver(design.design):
# The en pin will be over the vdd rail
vdd_yloc = self.en_inst.get_pin("vdd").cy()
self.add_layout_pin_segment_center(text="en",
layer="metal3",
layer="m3",
start=vector(0,vdd_yloc),
end=vector(self.width,vdd_yloc))
en_loc = self.en_inst.get_pin("A").center()
en_rail = vector(en_loc.x, vdd_yloc)
self.add_via_stack("metal1", "metal2", en_loc)
self.add_path("metal2", [en_loc, en_rail])
self.add_via_stack("metal2", "metal3", en_rail)
self.add_via_stack("m1", "m2", en_loc)
self.add_path("m2", [en_loc, en_rail])
self.add_via_stack("m2", "m3", en_rail)
# Start point in the track on the pin rail
en_track = vector(track_xoff, vdd_yloc)
self.add_via_stack("metal3", "metal4", en_track)
self.add_via_stack("m3", "m4", en_track)
# This is a U route to the right down then left
bl_en_loc = self.bl_inst.get_pin("en").center()
bl_en_track = vector(track_xoff, bl_en_loc.y)
self.add_via_stack("metal1", "metal3", bl_en_loc)
self.add_via_stack("m1", "m3", bl_en_loc)
br_en_loc = self.br_inst.get_pin("en").center()
br_en_track = vector(track_xoff, bl_en_loc.y)
self.add_via_stack("metal1", "metal3", br_en_loc)
self.add_via_stack("m1", "m3", br_en_loc)
# U shape
self.add_wire(("metal3","via3","metal4"),
self.add_wire(self.m3_stack,
[en_track, bl_en_track, bl_en_loc, bl_en_track, br_en_track, br_en_loc])
@ -284,7 +281,7 @@ class pwrite_driver(design.design):
# Continous vdd rail along with label.
vdd_pin=inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
layer="m1",
offset=vdd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
@ -292,7 +289,7 @@ class pwrite_driver(design.design):
# Continous gnd rail along with label.
gnd_pin=inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
layer="m1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())

View File

@ -31,6 +31,12 @@ class single_level_column_mux(pgate.pgate):
pgate.pgate.__init__(self, name)
def get_bl_names(self):
return "bl"
def get_br_names(self):
return "br"
def create_netlist(self):
self.add_modules()
self.add_pins()
@ -65,21 +71,21 @@ class single_level_column_mux(pgate.pgate):
# bl and br
self.add_layout_pin(text="bl",
layer="metal2",
layer="m2",
offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
self.add_layout_pin(text="br",
layer="metal2",
layer="m2",
offset=br_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
self.add_layout_pin(text="bl_out",
layer="metal2",
layer="m2",
offset=bl_pos,
height=self.pin_height)
self.add_layout_pin(text="br_out",
layer="metal2",
layer="m2",
offset=br_pos,
height=self.pin_height)
@ -126,22 +132,22 @@ class single_level_column_mux(pgate.pgate):
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=bl_pin.bc(),
directions=("V", "V"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=br_out_pin.uc(),
directions=("V", "V"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=nmos_upper_s_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=nmos_lower_d_pin.center(),
directions=("V", "V"))
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
self.add_path("metal1",
self.add_path("m1",
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
nmos_upper_d_pin.center()])
# halfway up, move over
@ -149,12 +155,12 @@ class single_level_column_mux(pgate.pgate):
+ nmos_upper_s_pin.bc().scale(0, 0.4)
mid2 = bl_out_pin.uc().scale(0, 0.4) \
+ nmos_upper_s_pin.bc().scale(1, 0.4)
self.add_path("metal2",
self.add_path("m2",
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
# br -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1
self.add_path("metal1",
self.add_path("m1",
[br_out_pin.uc(),
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
nmos_lower_s_pin.center()])
@ -163,7 +169,7 @@ class single_level_column_mux(pgate.pgate):
+ nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5) \
+ nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("metal2",
self.add_path("m2",
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
def add_wells(self):
@ -175,19 +181,15 @@ class single_level_column_mux(pgate.pgate):
# Add it to the right, aligned in between the two tx
active_pos = vector(self.bitcell.width,
self.nmos_upper.by() - 0.5 * self.poly_space)
self.add_via_center(layers=("active", "contact", "metal1"),
self.add_via_center(layers=self.active_stack,
offset=active_pos,
implant_type="p",
well_type="p")
# Add the M1->M2->M3 stack
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=active_pos)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=active_pos)
self.add_layout_pin_rect_center(text="gnd",
layer="metal3",
offset=active_pos)
# Add the M1->..->power_grid_layer stack
self.add_power_pin(name = "gnd",
loc = active_pos,
start_layer="m1")
# Add well enclosure over all the tx and contact
self.add_rect(layer="pwell",

View File

@ -5,7 +5,7 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from tech import drc, layer
from tech import drc, layer, preferred_directions
from contact import contact
from vector import vector
import debug
@ -26,6 +26,9 @@ class router_tech:
self.rail_track_width = rail_track_width
if len(self.layers) == 1:
if preferred_directions[self.layers[0]] != "H":
debug.warning("Using '{}' for horizontal routing, but it " \
"prefers vertical routing".format(self.layers[0]))
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
@ -35,7 +38,17 @@ class router_tech:
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
else:
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
(try_horiz_layer, self.via_layer_name, try_vert_layer) = self.layers
# figure out wich of the two layers prefers horizontal/vertical
# routing
if preferred_directions[try_horiz_layer] == "H" and preferred_directions[try_vert_layer] == "V":
self.horiz_layer_name = try_horiz_layer
self.vert_layer_name = try_vert_layer
else:
raise ValueError("Layer '{}' and '{}' are using the wrong " \
"preferred_directions '{}' and '{}'. Only "\
"('H', 'V') are supported")
via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width,via_connect.height)

View File

@ -31,6 +31,8 @@ class supply_grid_router(router):
This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file).
"""
start_time = datetime.now()
# Power rail width in minimum wire widths
self.rail_track_width = 3
@ -40,8 +42,8 @@ class supply_grid_router(router):
self.supply_rails = {}
# This is the same as above but as a sigle set for the all the rails
self.supply_rail_tracks = {}
print_time("Init supply router", datetime.now(), start_time, 3)
def create_routing_grid(self):

View File

@ -27,7 +27,7 @@ class vector3d():
self.x = x
self.y = y
self.z = z
self._hash = hash((self.x,self.y,self.z))
def __str__(self):
""" override print function output """
@ -96,7 +96,7 @@ class vector3d():
Note: This assumes that you DON'T CHANGE THE VECTOR or it will
break things.
"""
return hash((self.x,self.y,self.z))
return self._hash
def __rsub__(self, other):

View File

@ -5,21 +5,10 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys
from tech import drc, spice
import debug
from math import log,sqrt,ceil
import datetime
import getpass
import numpy as np
from vector import vector
from globals import OPTS, print_time
from sram_base import sram_base
from bank import bank
from contact import m2m3
from dff_buf_array import dff_buf_array
from dff_array import dff_array
from contact import m2_via
class sram_1bank(sram_base):
@ -261,18 +250,18 @@ class sram_1bank(sram_base):
# This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_path("m1", [control_clk_buf_pos, clk_steiner_pos])
self.add_via_center(layers=self.m1_stack,
offset=clk_steiner_pos)
# Note, the via to the control logic is taken care of above
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
self.add_wire(("m3","via2","m2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
self.add_wire(("m3","via2","m2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
@ -280,8 +269,8 @@ class sram_1bank(sram_base):
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
# In some designs, the steiner via will be too close to the mid_pos via
# so make the wire as wide as the contacts
self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height))
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
self.add_path("m2",[mid_pos, clk_steiner_pos], width=max(m2_via.width,m2_via.height))
self.add_wire(("m3","via2","m2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
if self.write_size:
wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk")
@ -289,8 +278,8 @@ class sram_1bank(sram_base):
mid_pos = vector(clk_steiner_pos.x, wmask_dff_clk_pos.y)
# In some designs, the steiner via will be too close to the mid_pos via
# so make the wire as wide as the contacts
self.add_path("metal2", [mid_pos, clk_steiner_pos], width=max(m2m3.width, m2m3.height))
self.add_wire(("metal3", "via2", "metal2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height))
self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
def route_control_logic(self):
@ -323,20 +312,20 @@ class sram_1bank(sram_base):
flop_pos = flop_pin.center()
bank_pos = bank_pin.center()
mid_pos = vector(bank_pos.x,flop_pos.y)
self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos,bank_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
self.add_wire(("m3","via2","m2"),[flop_pos, mid_pos,bank_pos])
self.add_via_center(layers=self.m2_stack,
offset=flop_pos)
def route_col_addr_dff(self):
""" Connect the output of the row flops to the bank pins """
""" Connect the output of the col flops to the bank pins """
for port in self.all_ports:
if port%2:
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size+2)*self.m1_pitch)
else:
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=offset,
names=bus_names,
@ -371,12 +360,12 @@ class sram_1bank(sram_base):
if self.write_size:
for x in dff_names:
pin_offset = self.data_dff_insts[port].get_pin(x).center()
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=pin_offset,
directions = ("V", "V"))
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=pin_offset)
self.add_via_center(layers=("metal3", "via3", "metal4"),
self.add_via_center(layers=self.m3_stack,
offset=pin_offset)
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
@ -387,20 +376,21 @@ class sram_1bank(sram_base):
pin_offset = self.bank_inst.get_pin(x).uc()
else:
pin_offset = self.bank_inst.get_pin(x).bc()
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=pin_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"),
self.add_via_center(layers=self.m2_stack,
offset=pin_offset)
self.add_via_center(layers=("metal3", "via3", "metal4"),
self.add_via_center(layers=self.m3_stack,
offset=pin_offset)
route_map = list(zip(bank_pins, dff_pins))
if self.write_size:
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=("metal3", "via3", "metal4"))
layer_stack = self.m3_stack
else:
self.create_horizontal_channel_route(route_map, offset)
layer_stack = self.m1_stack
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack)
def route_wmask_dff(self):
""" Connect the output of the wmask flops to the write mask AND array """
@ -415,7 +405,7 @@ class sram_1bank(sram_base):
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]
for x in dff_names:
offset_pin = self.wmask_dff_insts[port].get_pin(x).center()
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=offset_pin,
directions=("V", "V"))
@ -423,12 +413,14 @@ class sram_1bank(sram_base):
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
for x in bank_names:
offset_pin = self.bank_inst.get_pin(x).center()
self.add_via_center(layers=("metal1", "via1", "metal2"),
self.add_via_center(layers=self.m1_stack,
offset=offset_pin)
route_map = list(zip(bank_pins, dff_pins))
self.create_horizontal_channel_route(route_map,offset)
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=self.m1_stack)
def add_lvs_correspondence_points(self):

View File

@ -103,25 +103,25 @@ class sram_2bank(sram_base):
for n in self.control_logic_outputs + ["vdd", "gnd"]:
pins = self.control_logic_inst.get_pins(n)
for pin in pins:
if pin.layer=="metal2":
if pin.layer=="m2":
pin_pos = pin.bc()
break
rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
self.add_path("m2",[pin_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
# connect the control logic cross bar
for n in self.control_logic_outputs:
cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y)
self.add_via_center(("metal1","via1","metal2"),cross_pos)
self.add_via_center(self.m1_stack,cross_pos)
# connect the bank select signals to the vertical bus
for i in range(self.num_banks):
pin = self.bank_inst[i].get_pin("bank_sel")
pin_pos = pin.rc() if i==0 else pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y)
self.add_path("metal3",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.add_path("m3",[pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
def route_single_msb_address(self):
""" Route one MSB address bit for 2-bank SRAM """
@ -129,37 +129,37 @@ class sram_2bank(sram_base):
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1": continue
if vdd_pin.layer != "m1": continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(("metal1","via1","metal2"),down_pos,rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
self.add_path("m1",[vdd_pos,down_pos])
self.add_via_center(self.m1_stack,down_pos,rotate=90)
self.add_path("m2",[down_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
gnd_pins = self.msb_address_inst.get_pins("gnd")
# Only add the ground connection to the lowest metal2 rail in the flop array
# FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those
lowest_y = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2": continue
if gnd_pin.layer != "m2": continue
if lowest_y==None or gnd_pin.by()<lowest_y:
lowest_y=gnd_pin.by()
gnd_pos = gnd_pin.ur()
rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y)
self.add_path("metal2",[gnd_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
self.add_path("m2",[gnd_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
# connect the MSB flop to the address input bus
msb_pins = self.msb_address_inst.get_pins("din[0]")
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
if msb_pin.layer == "m3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.add_path("m3",[msb_pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect the output bar to select 0
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
@ -167,9 +167,9 @@ class sram_2bank(sram_base):
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect the output to select 1
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
@ -177,16 +177,16 @@ class sram_2bank(sram_base):
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
self.add_path("m1",[clk_pos,bend_pos,rail_pos])
@ -201,8 +201,8 @@ class sram_2bank(sram_base):
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.add_path("m2",[pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
self.route_single_msb_address()
@ -216,8 +216,8 @@ class sram_2bank(sram_base):
pin0_pos = self.bank_inst[0].get_pin(n).rc()
pin1_pos = self.bank_inst[1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.add_path("m3",[pin0_pos,pin1_pos])
self.add_via_center(self.m2_stack,rail_pos)
@ -232,9 +232,9 @@ class sram_2bank(sram_base):
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
layer="m2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
layer="m2",
offset=self.vert_control_bus_positions[n])

View File

@ -27,7 +27,7 @@ class sram_base(design, verilog, lef):
"""
def __init__(self, name, sram_config):
design.__init__(self, name)
lef.__init__(self, ["metal1", "metal2", "metal3", "metal4"])
lef.__init__(self, ["m1", "m2", "m3", "m4"])
verilog.__init__(self)
self.sram_config = sram_config
@ -148,14 +148,21 @@ class sram_base(design, verilog, lef):
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
elif "metal4" in tech.layer:
# Route a M3/M4 grid
from supply_grid_router import supply_grid_router as router
rtr=router(("metal3","via3","metal4"), self)
elif "metal3" in tech.layer:
from supply_tree_router import supply_tree_router as router
rtr=router(("metal3",), self)
grid_stack = set()
try:
from tech import power_grid
grid_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
if "m4" in tech.layer:
# Route a M3/M4 grid
grid_stack = self.m3_stack
elif "m3" in tech.layer:
grid_stack =("m3",)
from supply_grid_router import supply_grid_router as router
rtr=router(grid_stack, self)
rtr.route()
@ -200,14 +207,14 @@ class sram_base(design, verilog, lef):
self.control_bus_names[port].extend([wen, pen])
else:
self.control_bus_names[port].extend([sen, wen, pen])
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
self.vert_control_bus_positions = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
names=self.control_bus_names[port],
length=self.vertical_bus_height)
self.addr_bus_names=["A{0}[{1}]".format(port,i) for i in range(self.addr_size)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.addr_bus_offset,
names=self.addr_bus_names,
@ -215,7 +222,7 @@ class sram_base(design, verilog, lef):
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port,i) for i in range(self.num_banks)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names,
@ -224,7 +231,7 @@ class sram_base(design, verilog, lef):
# Horizontal data bus
self.data_bus_names = ["DATA{0}[{1}]".format(port,i) for i in range(self.word_size)]
self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3",
self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3",
pitch=self.m3_pitch,
offset=self.data_bus_offset,
names=self.data_bus_names,
@ -233,19 +240,19 @@ class sram_base(design, verilog, lef):
# Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1",
self.horz_control_bus_positions = self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset,
names=["vdd"],
length=self.supply_bus_width)
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
# the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset+vector(0,self.m1_pitch),
names=["gnd"],
length=self.supply_bus_width))
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.control_bus_offset,
names=self.control_bus_names[port],
@ -271,28 +278,25 @@ class sram_base(design, verilog, lef):
self.dff = factory.create(module_type="dff")
# Create the address and control flops (but not the clk)
from dff_array import dff_array
self.row_addr_dff = dff_array(name="row_addr_dff", rows=self.row_addr_size, columns=1)
self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1)
self.add_mod(self.row_addr_dff)
if self.col_addr_size > 0:
self.col_addr_dff = dff_array(name="col_addr_dff", rows=1, columns=self.col_addr_size)
self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size)
self.add_mod(self.col_addr_dff)
else:
self.col_addr_dff = None
self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size)
self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size)
self.add_mod(self.data_dff)
if self.write_size:
self.wmask_dff = dff_array(name="wmask_dff", rows=1, columns=self.num_wmasks)
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
self.add_mod(self.wmask_dff)
# Create the bank module (up to four are instantiated)
from bank import bank
self.bank = bank(self.sram_config,
name="bank")
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
self.add_mod(self.bank)
# Create bank decoder
@ -529,12 +533,12 @@ class sram_base(design, verilog, lef):
out_pos = dest_pin.uc()
# move horizontal first
self.add_wire(("metal3","via2","metal2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
if src_pin.layer=="metal1":
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_wire(("m3","via2","m2"),[in_pos, vector(out_pos.x,in_pos.y),out_pos])
if src_pin.layer=="m1":
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
if src_pin.layer in ["metal1","metal2"]:
self.add_via_center(layers=("metal2","via2","metal3"),
if src_pin.layer in ["m1","m2"]:
self.add_via_center(layers=self.m2_stack,
offset=in_pos)

View File

@ -24,9 +24,9 @@ class sram_config:
# This will get over-written when we determine the organization
self.words_per_row = words_per_row
self.compute_sizes()
def set_local_config(self, module):
""" Copy all of the member variables to the given module for convenience """

View File

@ -7,7 +7,6 @@
#
from globals import OPTS
class sram_factory:
"""
This is a factory pattern to create modules for usage in an SRAM.
@ -31,57 +30,108 @@ class sram_factory:
Clear the factory instances for testing.
"""
self.__init__()
def create(self, module_type, **kwargs):
def get_techmodule_type(self, module_type):
"""
A generic function to create a module with a given module_type.
The args are passed directly to the module constructor.
Try to load the custom tech module type.
"""
# if name!="":
# # This is a special case where the name and type don't match
# # Can't be overridden in the config file
# module_name = name
overridden = False
try:
from tech import tech_modules
real_module_type = tech_modules[module_type]
overridden = tech_modules.is_overridden(module_type)
except ImportError:
# If they didn't define these, then don't use the option types.
# Primarily for backward compatibility and simplicity of tech files.
real_module_type = module_type
except KeyError:
# If it wasn't a tech module type, we can ignore that too.
real_module_type = module_type
return (real_module_type, overridden)
def get_usermodule_type(self, module_type):
"""
Try to load the custom user module type. If the user hasn't specified
anything, we use the default from 'options.py'. If we cannot find anything, we
fall back to the original 'module_type'.
"""
overridden = False
if hasattr(OPTS, module_type):
# Retrieve the name from OPTS if it exists,
# otherwise just use the name
module_type = getattr(OPTS, module_type)
overridden = module_type in OPTS.overridden.keys()
return (module_type, overridden)
def is_duplicate_name(self, name):
for mods in self.objects.values():
for insts in mods:
if insts[1].name == name:
return True
return False
def create(self, module_type, module_name=None, **kwargs):
"""
A generic function to create a module with a given module_type.
The args are passed directly to the module constructor.
"""
tech_module_type, tm_overridden = self.get_techmodule_type(module_type)
user_module_type, um_overridden = self.get_usermodule_type(module_type)
# overridden user modules have priority
if um_overridden:
real_module_type = user_module_type
# then overridden tech modules
elif tm_overridden:
real_module_type = tech_module_type
# if nothing else works use the name generated by get_usermodule_type()
else:
real_module_type = user_module_type
# Either retrieve the already loaded module or load it
try:
mod = self.modules[module_type]
# Load a cached version from previous usage
mod = self.modules[real_module_type]
except KeyError:
# Dynamically load the module
import importlib
c = importlib.reload(__import__(module_type))
mod = getattr(c, module_type)
self.modules[module_type] = mod
self.module_indices[module_type] = 0
self.objects[module_type] = []
c = importlib.reload(__import__(real_module_type))
mod = getattr(c, real_module_type)
self.modules[real_module_type] = mod
self.module_indices[real_module_type] = 0
self.objects[real_module_type] = []
# Either retreive a previous object or create a new one
for obj in self.objects[module_type]:
for obj in self.objects[real_module_type]:
(obj_kwargs, obj_item) = obj
# Must have the same dictionary exactly (conservative)
if obj_kwargs == kwargs:
return obj_item
# Use the default name if there are default arguments
# This is especially for library cells so that the
# spice and gds files can be found.
if len(kwargs) > 0:
# Create a unique name and increment the index
module_name = "{0}_{1}".format(module_type,
self.module_indices[module_type])
self.module_indices[module_type] += 1
# If no prefered module name is provided, we generate one.
if module_name is None:
# Use the default name if there are default arguments
# This is especially for library cells so that the
# spice and gds files can be found.
if len(kwargs) > 0:
# Create a unique name and increment the index
module_name = "{0}_{1}".format(real_module_type,
self.module_indices[real_module_type])
self.module_indices[real_module_type] += 1
else:
module_name = real_module_type
else:
module_name = module_type
if self.is_duplicate_name(module_name):
raise ValueError("Modules with duplicate name are not allowed." \
" '{}'".format(module_name))
# type_str = "type={}".format(module_type)
# type_str = "type={}".format(real_module_type)
# name_str = "name={}".format(module_name)
# kwargs_str = "kwargs={}".format(str(kwargs))
# import debug
# debug.info(0, "New module:" + type_str + name_str + kwargs_str)
obj = mod(name=module_name, **kwargs)
self.objects[module_type].append((kwargs, obj))
self.objects[real_module_type].append((kwargs, obj))
return obj
def get_mods(self, module_type):

View File

@ -21,16 +21,15 @@ class library_drc_test(openram_test):
globals.init_openram(config_file)
import verify
(gds_dir, gds_files) = setup_files()
(gds_dir, allnames) = setup_files()
drc_errors = 0
debug.info(1, "\nPerforming DRC on: " + ", ".join(gds_files))
for f in gds_files:
name = re.sub('\.gds$', '', f)
gds_name = "{0}/{1}".format(gds_dir, f)
debug.info(1, "\nPerforming DRC on: " + ", ".join(allnames))
for f in allnames:
gds_name = "{0}/{1}.gds".format(gds_dir, f)
if not os.path.isfile(gds_name):
drc_errors += 1
debug.error("Missing GDS file: {}".format(gds_name))
drc_errors += verify.run_drc(name, gds_name)
drc_errors += verify.run_drc(f, gds_name)
# fails if there are any DRC errors on any cells
self.assertEqual(drc_errors, 0)
@ -42,12 +41,23 @@ def setup_files():
files = os.listdir(gds_dir)
nametest = re.compile("\.gds$", re.IGNORECASE)
gds_files = list(filter(nametest.search, files))
import tech
if tech.blackbox_bitcell:
# Ignore DRC of all bitcells
nametest = re.compile("cell", re.IGNORECASE)
gds_files = list(filter(lambda v: not nametest.search(v), gds_files))
return (gds_dir, gds_files)
tempnames = gds_files
# remove the .gds and .sp suffixes
for i in range(len(tempnames)):
gds_files[i] = re.sub('\.gds$', '', tempnames[i])
try:
from tech import blackbox_cells
nameset = list(set(tempnames) - set(blackbox_cells))
except ImportError:
# remove duplicate base names
nameset = set(tempnames)
allnames = list(nameset)
return (gds_dir, allnames)
# run the test from the command line

View File

@ -62,8 +62,13 @@ def setup_files():
tempnames[i] = re.sub('\.gds$', '', tempnames[i])
tempnames[i] = re.sub('\.sp$', '', tempnames[i])
# remove duplicate base names
nameset = set(tempnames)
try:
from tech import blackbox_cells
nameset = list(set(tempnames) - set(blackbox_cells))
except ImportError:
# remove duplicate base names
nameset = set(tempnames)
allnames = list(nameset)
return (gds_dir, sp_dir, allnames)

View File

@ -21,12 +21,16 @@ class contact_test(openram_test):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
for layer_stack in [("metal1", "via1", "metal2"), ("poly", "contact", "metal1")]:
from tech import active_stack, poly_stack, beol_stacks
# Don't do active because of nwell contact rules
# Don't do metal3 because of min area rules
for layer_stack in [poly_stack] + [beol_stacks[0]]:
stack_name = ":".join(map(str, layer_stack))
# Check single 1 x 1 contact"
debug.info(2, "1 x 1 {} test".format(stack_name))
c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1))
c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("H", "H"))
self.local_drc_check(c)
# Check single 1 x 1 contact"
@ -43,6 +47,10 @@ class contact_test(openram_test):
debug.info(2, "1 x 1 {} test".format(stack_name))
c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(1, 1), directions=("V","V"))
self.local_drc_check(c)
# Only do multiple contacts for BEOL
for layer_stack in beol_stacks:
stack_name = ":".join(map(str, layer_stack))
# check vertical array with one in the middle and two ends
debug.info(2, "1 x 3 {} test".format(stack_name))
@ -59,6 +67,25 @@ class contact_test(openram_test):
c = factory.create(module_type="contact", layer_stack=layer_stack, dimensions=(3, 3))
self.local_drc_check(c)
# Test the well taps
# check vertical array with one in the middle and two ends
layer_stack = active_stack
stack_name = ":".join(map(str, layer_stack))
debug.info(2, "1 x 1 {} nwell".format(stack_name))
c = factory.create(module_type="contact",
layer_stack=layer_stack,
implant_type="n",
well_type="n")
self.local_drc_check(c)
debug.info(2, "1 x 1 {} pwell".format(stack_name))
c = factory.create(module_type="contact",
layer_stack=layer_stack,
implant_type="p",
well_type="p")
self.local_drc_check(c)
globals.end_openram()

View File

@ -23,8 +23,8 @@ class path_test(openram_test):
import tech
import design
min_space = 2 * tech.drc["minwidth_metal1"]
layer_stack = ("metal1")
min_space = 2 * tech.drc["minwidth_m1"]
layer_stack = ("m1")
# checks if we can retrace a path
position_list = [[0,0],
[0, 3 * min_space ],
@ -37,8 +37,8 @@ class path_test(openram_test):
self.local_drc_check(w)
min_space = 2 * tech.drc["minwidth_metal1"]
layer_stack = ("metal1")
min_space = 2 * tech.drc["minwidth_m1"]
layer_stack = ("m1")
old_position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
@ -53,8 +53,8 @@ class path_test(openram_test):
wire_path.wire_path(w,layer_stack, position_list)
self.local_drc_check(w)
min_space = 2 * tech.drc["minwidth_metal2"]
layer_stack = ("metal2")
min_space = 2 * tech.drc["minwidth_m2"]
layer_stack = ("m2")
old_position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
@ -69,8 +69,8 @@ class path_test(openram_test):
wire_path.wire_path(w, layer_stack, position_list)
self.local_drc_check(w)
min_space = 2 * tech.drc["minwidth_metal3"]
layer_stack = ("metal3")
min_space = 2 * tech.drc["minwidth_m3"]
layer_stack = ("m3")
position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],

View File

@ -8,11 +8,11 @@
#
import unittest
from testutils import *
import sys,os
import sys
import os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
import debug
class wire_test(openram_test):
@ -22,107 +22,33 @@ class wire_test(openram_test):
import wire
import tech
import design
min_space = 2 * (tech.drc["minwidth_poly"] +
tech.drc["minwidth_metal1"])
layer_stack = ("poly", "contact", "metal1")
old_position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
position_list = [[x-min_space, y-min_space] for x,y in old_position_list]
w = design.design("wire_test1")
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
min_space = 2 * (tech.drc["minwidth_poly"] +
tech.drc["minwidth_metal1"])
layer_stack = ("poly", "contact", "metal1")
old_position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
position_list = [[x+min_space, y+min_space] for x,y in old_position_list]
w = design.design("wire_test2")
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
layer_stacks = [tech.poly_stack] + tech.beol_stacks
min_space = 2 * (tech.drc["minwidth_metal2"] +
tech.drc["minwidth_metal1"])
layer_stack = ("metal1", "via1", "metal2")
position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
w = design.design("wire_test3")
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
for reverse in [False, True]:
for stack in layer_stacks:
if reverse:
layer_stack = stack[::-1]
else:
layer_stack = stack
# Just make a conservative spacing. Make it wire pitch instead?
min_space = 2 * (tech.drc["minwidth_{}".format(layer_stack[0])] +
tech.drc["minwidth_{}".format(layer_stack[2])])
min_space = 2 * (tech.drc["minwidth_metal2"] +
tech.drc["minwidth_metal1"])
layer_stack = ("metal2", "via1", "metal1")
position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
w = design.design("wire_test4")
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
min_space = 2 * (tech.drc["minwidth_metal2"] +
tech.drc["minwidth_metal3"])
layer_stack = ("metal2", "via2", "metal3")
position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
position_list.reverse()
w = design.design("wire_test5")
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
min_space = 2 * (tech.drc["minwidth_metal2"] +
tech.drc["minwidth_metal3"])
layer_stack = ("metal3", "via2", "metal2")
position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
position_list.reverse()
w = design.design("wire_test6")
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
position_list = [[0, 0],
[0, 3 * min_space],
[1 * min_space, 3 * min_space],
[4 * min_space, 3 * min_space],
[4 * min_space, 0],
[7 * min_space, 0],
[7 * min_space, 4 * min_space],
[-1 * min_space, 4 * min_space],
[-1 * min_space, 0]]
position_list = [[x - min_space, y - min_space] for x, y in position_list]
w = design.design("wire_test_{}".format("_".join(layer_stack)))
wire.wire(w, layer_stack, position_list)
self.local_drc_check(w)
globals.end_openram()

View File

@ -7,12 +7,37 @@
#
import os
from design_rules import *
from module_type import *
from custom_cell_properties import cell_properties
"""
File containing the process technology parameters for FreePDK 45nm.
"""
#GDS file info
###################################################
# Custom modules
###################################################
# This uses the default classes to instantiate module from
# '$OPENRAM_HOME/compiler/modules'.
# Using tech_modules['cellname'] you can override each class by providing a custom
# implementation in '$OPENRAM_TECHDIR/modules/'
# For example: tech_modules['contact'] = 'contact_freepdk45'
tech_modules = module_type()
###################################################
# Custom cell properties
###################################################
cell_properties = cell_properties()
cell_properties.bitcell.mirror.x = True
cell_properties.bitcell.mirror.y = False
###################################################
# GDS file info
###################################################
GDS = {}
# gds units
# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first
@ -29,7 +54,40 @@ GDS["unit"] = (0.0005,1e-9)
GDS["zoom"] = 0.05
###################################################
##GDS Layer Map
# Interconnect stacks
###################################################
poly_stack = ("poly", "contact", "m1")
active_stack = ("active", "contact", "m1")
m1_stack = ("m1", "via1", "m2")
m2_stack = ("m2", "via2", "m3")
m3_stack = ("m3", "via3", "m4")
# The FEOL stacks get us up to m1
feol_stacks = [poly_stack,
active_stack]
# The BEOL stacks are m1 and up
beol_stacks = [m1_stack,
m2_stack,
m3_stack]
layer_stacks = feol_stacks + beol_stacks
preferred_directions = {"poly": "V",
"active": "V",
"m1": "H",
"m2": "V",
"m3": "H",
"m4": "V"}
###################################################
# Power grid
###################################################
# Use M3/M4
power_grid = m3_stack
###################################################
# GDS Layer Map
###################################################
# create the GDS layer map
@ -44,36 +102,31 @@ layer["vtg"] = (6, 0)
layer["vth"] = (7, 0)
layer["thkox"] = (8, 0)
layer["poly"] = (9, 0)
layer["active_contact"] = (10, 0)
layer["poly_contact"] = (10, 0)
layer["metal1"] = (11, 0)
layer["contact"] = (10, 0)
layer["m1"] = (11, 0)
layer["via1"] = (12, 0)
layer["metal2"] = (13, 0)
layer["m2"] = (13, 0)
layer["via2"] = (14, 0)
layer["metal3"] = (15, 0)
layer["m3"] = (15, 0)
layer["via3"] = (16, 0)
layer["metal4"] = (17, 0)
layer["m4"] = (17, 0)
layer["via4"] = (18, 0)
layer["metal5"] = (19, 0)
layer["m5"] = (19, 0)
layer["via5"] = (20, 0)
layer["metal6"] = (21, 0)
layer["m6"] = (21, 0)
layer["via6"] = (22, 0)
layer["metal7"] = (23, 0)
layer["m7"] = (23, 0)
layer["via7"] = (24, 0)
layer["metal8"] = (25, 0)
layer["m8"] = (25, 0)
layer["via8"] = (26, 0)
layer["metal9"] = (27, 0)
layer["m9"] = (27, 0)
layer["via9"] = (28, 0)
layer["metal10"] = (29, 0)
layer["m10"] = (29, 0)
layer["text"] = (239, 0)
layer["boundary"]= (239, 0)
###################################################
##END GDS Layer Map
###################################################
###################################################
##DRC/LVS Rules Setup
# DRC/LVS Rules Setup
###################################################
#technology parameter
@ -105,20 +158,26 @@ drc["minlength_channel"] = 0.05
# WELL.2 Minimum spacing of nwell/pwell at different potential
drc["pwell_to_nwell"] = 0.225
# WELL.3 Minimum spacing of nwell/pwell at the same potential
drc["well_to_well"] = 0.135
# WELL.4 Minimum width of nwell/pwell
drc["minwidth_well"] = 0.2
drc.add_layer("nwell",
width = 0.2,
spacing = 0.135)
drc.add_layer("pwell",
width = 0.2,
spacing = 0.135)
# POLY.1 Minimum width of poly
drc["minwidth_poly"] = 0.05
# POLY.2 Minimum spacing of poly AND active
drc["poly_to_poly"] = 0.14
drc.add_layer("poly",
width = 0.05,
spacing = 0.14)
# POLY.3 Minimum poly extension beyond active
drc["poly_extend_active"] = 0.055
# Not a rule
drc["poly_to_poly_contact"] = 0.075
drc["poly_to_contact"] = 0.075
# POLY.4 Minimum enclosure of active around gate
drc["active_enclosure_gate"] = 0.07
drc["active_enclose_gate"] = 0.07
# POLY.5 Minimum spacing of field poly to active
drc["poly_to_active"] = 0.05
# POLY.6 Minimum Minimum spacing of field poly
@ -126,165 +185,169 @@ drc["poly_to_field_poly"] = 0.075
# Not a rule
drc["minarea_poly"] = 0.0
# ACTIVE.2 Minimum spacing of active
drc["active_to_body_active"] = 0.08
# ACTIVE.1 Minimum width of active
drc["minwidth_active"] = 0.09
# Not a rule
drc["active_to_active"] = 0
# ACTIVE.2 Minimum spacing of active
drc.add_layer("active",
width = 0.09,
spacing = 0.08)
# ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active
drc["well_enclosure_active"] = 0.055
# Reserved for asymmetric enclosures
drc["well_extend_active"] = 0.055
# Not a rule
drc["minarea_active"] = 0
drc.add_enclosure("nwell",
layer = "active",
enclosure = 0.055)
drc.add_enclosure("pwell",
layer = "active",
enclosure = 0.055)
# IMPLANT.1 Minimum spacing of nimplant/ pimplant to channel
drc["implant_to_channel"] = 0.07
# Not a rule
drc["implant_enclosure_active"] = 0
drc.add_enclosure("implant",
layer = "active",
enclosure = 0)
# Not a rule
drc["implant_enclosure_contact"] = 0
drc.add_enclosure("implant",
layer = "contact",
enclosure = 0)
# IMPLANT.2 Minimum spacing of nimplant/ pimplant to contact
drc["implant_to_contact"] = 0.025
# IMPLANT.3 Minimum width/ spacing of nimplant/ pimplant
drc["implant_to_implant"] = 0.045
# IMPLANT.4 Minimum width/ spacing of nimplant/ pimplant
drc["minwidth_implant"] = 0.045
drc.add_layer("implant",
width = 0.045,
spacing = 0.045)
# CONTACT.1 Minimum width of contact
drc["minwidth_active_contact"] = 0.065
# CONTACT.2 Minimum spacing of contact
drc["active_contact_to_active_contact"] = 0.075
drc.add_layer("contact",
width = 0.065,
spacing = 0.075)
# CONTACT.4 Minimum enclosure of active around contact
drc["active_enclosure_active_contact"] = 0.005
# Reserved for asymmetric enclosures
drc["active_extend_active_contact"] = 0.005
drc.add_enclosure("active",
layer = "contact",
enclosure = 0.005)
# CONTACT.6 Minimum spacing of contact and gate
drc["active_contact_to_gate"] = 0.0375 #changed from 0.035
drc["contact_to_gate"] = 0.0375 #changed from 0.035
# CONTACT.7 Minimum spacing of contact and poly
drc["active_contact_to_poly"] = 0.090
drc["contact_to_poly"] = 0.090
# CONTACT.1 Minimum width of contact
drc["minwidth_poly_contact"] = 0.065
# CONTACT.2 Minimum spacing of contact
drc["poly_contact_to_poly_contact"] = 0.075
# Reserved for asymmetric enclosures
drc["poly_extend_contact"] = 0.005
drc.add_layer("contact",
width = 0.065,
spacing = 0.075)
# CONTACT.5 Minimum enclosure of poly around contact
drc["poly_enclosure_poly_contact"] = 0.005
# Reserved for asymmetric enclosures
drc["poly_extend_poly_contact"] = 0.005
drc.add_enclosure("poly",
layer = "contact",
enclosure = 0.005)
# CONTACT.6 Minimum spacing of contact and gate
drc["poly_contact_to_gate"] = 0.0375 #changed from 0.035
drc["contact_to_gate"] = 0.0375 #changed from 0.035
# CONTACT.7 Minimum spacing of contact and poly
drc["poly_contact_to_poly"] = 0.090
drc["contact_to_poly"] = 0.090
# METAL1.1 Minimum width of metal1
drc["minwidth_metal1"] = 0.065
# METAL1.2 Minimum spacing of metal1
drc["metal1_to_metal1"] = 0.065
drc.add_layer("m1",
width = 0.065,
spacing = 0.065)
# METAL1.3 Minimum enclosure around contact on two opposite sides
drc["metal1_enclosure_active_contact"] = 0
# Reserved for asymmetric enclosures
drc["metal1_extend_active_contact"] = 0.035
# METAL1.3 Minimum enclosure around contact on two opposite sides
drc["metal1_enclosure_poly_contact"] = 0
# Reserved for asymmetric enclosures
drc["metal1_extend_poly_contact"] = 0.035
drc.add_enclosure("m1",
layer = "contact",
enclosure = 0,
extension = 0.035)
# METAL1.4 inimum enclosure around via1 on two opposite sides
drc["metal1_extend_via1"] = 0.035
# Reserved for asymmetric enclosures
drc["metal1_enclosure_via1"] = 0
# Not a rule
drc["minarea_metal1"] = 0
drc.add_enclosure("m1",
layer = "via1",
enclosure = 0,
extension = 0.035)
# VIA1.1 Minimum width of via1
drc["minwidth_via1"] = 0.065
# VIA1.2 Minimum spacing of via1
drc["via1_to_via1"] = 0.075
drc.add_layer("via1",
width = 0.065,
spacing = 0.075)
# METALINT.1 Minimum width of intermediate metal
drc["minwidth_metal2"] = 0.07
# METALINT.2 Minimum spacing of intermediate metal
drc["metal2_to_metal2"] = 0.07
drc.add_layer("m2",
width = 0.07,
spacing = 0.07)
# METALINT.3 Minimum enclosure around via1 on two opposite sides
drc["metal2_extend_via1"] = 0.035
# Reserved for asymmetric enclosures
drc["metal2_enclosure_via1"] = 0
drc.add_enclosure("m2",
layer = "via1",
enclosure = 0,
extension = 0.035)
# METALINT.4 Minimum enclosure around via[2-3] on two opposite sides
drc["metal2_extend_via2"] = 0.035
# Reserved for asymmetric enclosures
drc["metal2_enclosure_via2"] = 0
# Not a rule
drc["minarea_metal2"] = 0
drc.add_enclosure("m2",
layer = "via2",
enclosure = 0,
extension = 0.035)
# VIA2-3.1 Minimum width of Via[2-3]
drc["minwidth_via2"] = 0.065
# VIA2-3.2 Minimum spacing of Via[2-3]
drc["via2_to_via2"] = 0.075
drc.add_layer("via2",
width = 0.065,
spacing = 0.075)
# METALINT.1 Minimum width of intermediate metal
drc["minwidth_metal3"] = 0.07
# METALINT.2 Minimum spacing of intermediate metal
#drc["metal3_to_metal3"] = 0.07
# Minimum spacing of metal3 wider than 0.09 & longer than 0.3 = 0.09
# Minimum spacing of metal3 wider than 0.27 & longer than 0.9 = 0.27
# Minimum spacing of metal3 wider than 0.5 & longer than 1.8 = 0.5
# Minimum spacing of metal3 wider than 0.9 & longer than 2.7 = 0.9
# Minimum spacing of metal3 wider than 1.5 & longer than 4.0 = 1.5
drc["metal3_to_metal3"] = drc_lut({(0.00, 0.0) : 0.07,
(0.09, 0.3) : 0.09,
(0.27, 0.9) : 0.27,
(0.50, 1.8) : 0.5,
(0.90, 2.7) : 0.9,
(1.50, 4.0) : 1.5})
# Minimum spacing of m3 wider than 0.09 & longer than 0.3 = 0.09
# Minimum spacing of m3 wider than 0.27 & longer than 0.9 = 0.27
# Minimum spacing of m3 wider than 0.5 & longer than 1.8 = 0.5
# Minimum spacing of m3 wider than 0.9 & longer than 2.7 = 0.9
# Minimum spacing of m3 wider than 1.5 & longer than 4.0 = 1.5
drc.add_layer("m3",
width = 0.07,
spacing = drc_lut({(0.00, 0.0) : 0.07,
(0.09, 0.3) : 0.09,
(0.27, 0.9) : 0.27,
(0.50, 1.8) : 0.5,
(0.90, 2.7) : 0.9,
(1.50, 4.0) : 1.5}))
# METALINT.3 Minimum enclosure around via1 on two opposite sides
drc["metal3_extend_via2"] = 0.035
# Reserved for asymmetric enclosures
drc["metal3_enclosure_via2"] = 0
drc.add_enclosure("m3",
layer = "via2",
enclosure = 0,
extension = 0.035)
# METALINT.4 Minimum enclosure around via[2-3] on two opposite sides
drc["metal3_extend_via3"]=0.035
# Reserved for asymmetric enclosures
drc["metal3_enclosure_via3"] = 0
# Not a rule
drc["minarea_metal3"] = 0
drc.add_enclosure("m3",
layer = "via3",
enclosure = 0,
extension = 0.035)
# VIA2-3.1 Minimum width of Via[2-3]
drc["minwidth_via3"] = 0.07
# VIA2-3.2 Minimum spacing of Via[2-3]
drc["via3_to_via3"] = 0.085
drc.add_layer("via3",
width = 0.07,
spacing = 0.085)
# METALSMG.1 Minimum width of semi-global metal
drc["minwidth_metal4"] = 0.14
# METALSMG.2 Minimum spacing of semi-global metal
#drc["metal4_to_metal4"] = 0.14
# Minimum spacing of metal4 wider than 0.27 & longer than 0.9 = 0.27
# Minimum spacing of metal4 wider than 0.5 & longer than 1.8 = 0.5
# Minimum spacing of metal4 wider than 0.9 & longer than 2.7 = 0.9
# Minimum spacing of metal4 wider than 1.5 & longer than 4.0 = 1.5
drc["metal4_to_metal4"] = drc_lut({(0.00, 0.0) : 0.14,
(0.27, 0.9) : 0.27,
(0.50, 1.8) : 0.5,
(0.90, 2.7) : 0.9,
(1.50, 4.0) : 1.5})
# Minimum spacing of m4 wider than 0.27 & longer than 0.9 = 0.27
# Minimum spacing of m4 wider than 0.5 & longer than 1.8 = 0.5
# Minimum spacing of m4 wider than 0.9 & longer than 2.7 = 0.9
# Minimum spacing of m4 wider than 1.5 & longer than 4.0 = 1.5
drc.add_layer("m4",
width = 0.14,
spacing = drc_lut({(0.00, 0.0) : 0.14,
(0.27, 0.9) : 0.27,
(0.50, 1.8) : 0.5,
(0.90, 2.7) : 0.9,
(1.50, 4.0) : 1.5}))
# METALSMG.3 Minimum enclosure around via[3-6] on two opposite sides
drc["metal4_extend_via3"] = 0.0025
# Reserved for asymmetric enclosure
drc["metal4_enclosure_via3"] = 0.0025
# Not a rule
drc["minarea_metal4"] = 0
drc.add_enclosure("m4",
layer = "via3",
enclosure = 0.0025)
# Metal 5-10 are ommitted
###################################################
##END DRC/LVS Rules
###################################################
###################################################
##Spice Simulation Parameters
# Spice Simulation Parameters
###################################################
#spice info
@ -347,11 +410,7 @@ parameter["sa_inv_nmos_size"] = 0.27 #micro-meters
parameter["bitcell_drain_cap"] = 0.1 #In Femto-Farad, approximation of drain capacitance
###################################################
##END Spice Simulation Parameters
###################################################
###################################################
##BEGIN Technology Tool Preferences
# Technology Tool Preferences
###################################################
drc_name = "calibre"
@ -359,7 +418,3 @@ lvs_name = "calibre"
pex_name = "calibre"
blackbox_bitcell = False
###################################################
##END Technology Tool Preferences
###################################################

View File

@ -1,9 +1,24 @@
import os
from design_rules import *
from module_type import *
from custom_cell_properties import CellProperties
"""
File containing the process technology parameters for SCMOS 3me, subm, 180nm.
"""
# This uses the default classes to instantiate module from
# '$OPENRAM_HOME/compiler/modules'.
# Using tech_modules['cellname'] you can override each class by providing a custom
# implementation in '$OPENRAM_TECHDIR/modules/'
# For example: tech_modules['contact'] = 'contact_scn3me'
tech_modules = ModuleType()
###################################################
# Custom cell properties
###################################################
cell_properties = CellProperties()
cell_properties.bitcell.mirror.x = True
cell_properties.bitcell.mirror.y = False
#GDS file info
GDS={}

View File

@ -7,12 +7,34 @@
#
import os
from design_rules import *
from module_type import *
from custom_cell_properties import cell_properties
"""
File containing the process technology parameters for SCMOS 4m, 0.35um
"""
#GDS file info
###################################################
# Custom modules
###################################################
# This uses the default classes to instantiate module from
# '$OPENRAM_HOME/compiler/modules'.
# Using tech_modules['cellname'] you can override each class by providing a custom
# implementation in '$OPENRAM_TECHDIR/modules/'
# For example: tech_modules['contact'] = 'contact_scn4m'
tech_modules = module_type()
###################################################
# Custom cell properties
###################################################
cell_properties = cell_properties()
cell_properties.bitcell.mirror.x = True
cell_properties.bitcell.mirror.y = False
###################################################
# GDS file info
###################################################
GDS={}
# gds units
# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first
@ -28,6 +50,39 @@ GDS["unit"]=(0.001,1e-6)
# default label zoom
GDS["zoom"] = 0.5
###################################################
# Interconnect stacks
###################################################
poly_stack = ("poly", "poly_contact", "m1")
active_stack = ("active", "active_contact", "m1")
m1_stack = ("m1", "via1", "m2")
m2_stack = ("m2", "via2", "m3")
m3_stack = ("m3", "via3", "m4")
# The FEOL stacks get us up to m1
feol_stacks = [poly_stack,
active_stack]
# The BEOL stacks are m1 and up
beol_stacks = [m1_stack,
m2_stack,
m3_stack]
layer_stacks = feol_stacks + beol_stacks
preferred_directions = {"poly": "V",
"active": "V",
"m1": "H",
"m2": "V",
"m3": "H",
"m4": "V"}
###################################################
# Power grid
###################################################
# Use M3/M4
power_grid = m3_stack
###################################################
##GDS Layer Map
@ -35,32 +90,26 @@ GDS["zoom"] = 0.5
# create the GDS layer map
layer={}
layer["vtg"] = None
layer["vth"] = None
layer["pwell"] = (41, 0)
layer["nwell"] = (42, 0)
layer["active"] = (43, 0)
layer["pimplant"] = (44, 0)
layer["nimplant"] = (45, 0)
layer["poly"] = (46, 0)
layer["active_contact"] = (48, 0)
layer["poly_contact"] = (47, 0)
layer["metal1"] = (49, 0)
layer["active_contact"] = (48, 0)
layer["m1"] = (49, 0)
layer["via1"] = (50, 0)
layer["metal2"] = (51, 0)
layer["m2"] = (51, 0)
layer["via2"] = (61, 0)
layer["metal3"] = (62, 0)
layer["m3"] = (62, 0)
layer["via3"] = (30, 0)
layer["metal4"] = (31, 0)
layer["m4"] = (31, 0)
layer["text"] = (63, 0)
layer["boundary"] = (63, 0)
###################################################
##END GDS Layer Map
###################################################
###################################################
##DRC/LVS Rules Setup
# DRC/LVS Rules Setup
###################################################
_lambda_ = 0.2
@ -90,163 +139,173 @@ drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/scn3me_subm/layers.map"
drc["minwidth_tx"] = 4*_lambda_
drc["minlength_channel"] = 2*_lambda_
# 1.3 Minimum spacing between wells of same type (if both are drawn)
drc["well_to_well"] = 6*_lambda_
# 1.4 Minimum spacing between wells of different type (if both are drawn)
# 1.4 Minimum spacing between wells of different type (if both are drawn)
drc["pwell_to_nwell"] = 0
# 1.1 Minimum width
drc["minwidth_well"] = 12*_lambda_
# 1.3 Minimum spacing between wells of same type (if both are drawn)
# 1.1 Minimum width
drc.add_layer("nwell",
width = 12*_lambda_,
spacing = 6*_lambda_)
drc.add_layer("pwell",
width = 12*_lambda_,
spacing = 6*_lambda_)
# 3.1 Minimum width
drc["minwidth_poly"] = 2*_lambda_
# 3.2 Minimum spacing over active
drc["poly_to_poly"] = 3*_lambda_
drc.add_layer("poly",
width = 2*_lambda_,
spacing = 3*_lambda_)
# 3.3 Minimum gate extension of active
drc["poly_extend_active"] = 2*_lambda_
# 5.5.b Minimum spacing between poly contact and other poly (alternative rules)
drc["poly_to_poly_contact"] = 4*_lambda_
drc["poly_to_contact"] = 4*_lambda_
# ??
drc["active_enclosure_gate"] = 0.0
drc["active_enclose_gate"] = 0.0
# 3.5 Minimum field poly to active
drc["poly_to_active"] = _lambda_
# 3.2.a Minimum spacing over field poly
drc["poly_to_field_poly"] = 3*_lambda_
# Not a rule
drc["minarea_poly"] = 0.0
# ??
drc["active_to_body_active"] = 4*_lambda_ # Fix me
# 2.1 Minimum width
drc["minwidth_active"] = 3*_lambda_
# 2.2 Minimum spacing
drc["active_to_active"] = 3*_lambda_
drc.add_layer("active",
width = 3*_lambda_,
spacing = 4*_lambda_)
# 2.3 Source/drain active to well edge
drc["well_enclosure_active"] = 6*_lambda_
# Reserved for asymmetric enclosures
drc["well_extend_active"] = 6*_lambda_
# Not a rule
drc["minarea_active"] = 0.0
drc.add_enclosure("nwell",
layer = "active",
enclosure = 6*_lambda_)
drc.add_enclosure("pwell",
layer = "active",
enclosure = 6*_lambda_)
# 4.1 Minimum select spacing to channel of transistor to ensure adequate source/drain width
drc["implant_to_channel"] = 3*_lambda_
# 4.2 Minimum select overlap of active
drc["implant_enclosure_active"] = 2*_lambda_
drc.add_enclosure("implant",
layer = "active",
enclosure = 2*_lambda_)
# 4.3 Minimum select overlap of contact
drc["implant_enclosure_contact"] = _lambda_
drc.add_enclosure("implant",
layer = "contact",
enclosure = _lambda_)
# Not a rule
drc["implant_to_contact"] = 0
# Not a rule
drc["implant_to_implant"] = 0
# Not a rule
drc["minwidth_implant"] = 0
drc.add_layer("implant",
width = 0,
spacing = 0)
# 6.1 Exact contact size
drc["minwidth_active_contact"] = 2*_lambda_
# 5.3 Minimum contact spacing
drc["active_contact_to_active_contact"] = 3*_lambda_
# 6.2.b Minimum active overlap
drc["active_enclosure_active_contact"] = _lambda_
# Reserved for asymmetric enclosure
drc["active_extend_active_contact"] = _lambda_
drc.add_layer("active_contact",
width = 2*_lambda_,
spacing = 3*_lambda_)
# 6.2.b Minimum active overlap
drc.add_enclosure("active",
layer = "active_contact",
enclosure = _lambda_)
drc.add_enclosure("active",
layer = "contact",
enclosure = _lambda_)
# Reserved for other technologies
drc["active_contact_to_gate"] = 2*_lambda_
drc["contact_to_gate"] = 2*_lambda_
# 5.4 Minimum spacing to gate of transistor
drc["active_contact_to_poly"] = 2*_lambda_
drc["contact_to_poly"] = 2*_lambda_
# 6.1 Exact contact size
drc["minwidth_poly_contact"] = 2*_lambda_
# 5.3 Minimum contact spacing
drc["poly_contact_to_poly_contact"] = 3*_lambda_
drc.add_layer("poly_contact",
width = 2*_lambda_,
spacing = 3*_lambda_)
# 5.2.b Minimum poly overlap
drc["poly_enclosure_poly_contact"] = _lambda_
# Reserved for asymmetric enclosures
drc["poly_extend_poly_contact"] = _lambda_
drc.add_enclosure("poly",
layer = "poly_contact",
enclosure = _lambda_)
# Reserved for other technologies
drc["poly_contact_to_gate"] = 2*_lambda_
# 5.4 Minimum spacing to gate of transistor
drc["poly_contact_to_poly"] = 2*_lambda_
# 7.1 Minimum width
drc["minwidth_metal1"] = 3*_lambda_
# 7.2 Minimum spacing
drc["metal1_to_metal1"] = 3*_lambda_
drc.add_layer("m1",
width = 3*_lambda_,
spacing = 3*_lambda_)
# 7.3 Minimum overlap of any contact
drc["metal1_enclosure_active_contact"] = _lambda_
# Reserved for asymmetric enclosure
drc["metal1_extend_active_contact"] = _lambda_
# 7.3 Minimum overlap of any contact
drc["metal1_enclosure_poly_contact"] = _lambda_
# Reserved for asymmetric enclosure
drc["metal1_extend_poly_contact"] = _lambda_
# 8.3 Minimum overlap by metal1
drc["metal1_enclosure_via1"] = _lambda_
# Reserve for asymmetric enclosures
drc["metal1_extend_via1"] = _lambda_
# Not a rule
drc["minarea_metal1"] = 0
drc.add_enclosure("m1",
layer = "poly_contact",
enclosure = _lambda_)
drc.add_enclosure("m1",
layer = "active_contact",
enclosure = _lambda_)
# 8.3 Minimum overlap by m1
drc.add_enclosure("m1",
layer = "via1",
enclosure = _lambda_)
# 8.1 Exact size
drc["minwidth_via1"] = 2*_lambda_
# 8.2 Minimum via1 spacing
drc["via1_to_via1"] = 3*_lambda_
drc.add_layer("via1",
width = 2*_lambda_,
spacing = 3*_lambda_)
# 9.1 Minimum width
drc["minwidth_metal2"] = 3*_lambda_
# 9.2 Minimum spacing
drc["metal2_to_metal2"] = 3*_lambda_
drc.add_layer("m2",
width = 3*_lambda_,
spacing = 3*_lambda_)
# 9.3 Minimum overlap of via1
drc["metal2_extend_via1"] = _lambda_
# Reserved for asymmetric enclosures
drc["metal2_enclosure_via1"] = _lambda_
# 14.3 Minimum overlap by metal2
drc["metal2_extend_via2"] = _lambda_
# Reserved for asymmetric enclosures
drc["metal2_enclosure_via2"] = _lambda_
# Not a rule
drc["minarea_metal2"] = 0
drc.add_enclosure("m2",
layer = "via1",
enclosure = _lambda_)
# 14.3 Minimum overlap by m2
drc.add_enclosure("m2",
layer = "via2",
enclosure = _lambda_)
# 14.1 Exact size
drc["minwidth_via2"] = 2*_lambda_
# 14.2 Minimum spacing
drc["via2_to_via2"] = 3*_lambda_
drc.add_layer("via2",
width = 2*_lambda_,
spacing = 3*_lambda_)
# 15.1 Minimum width
drc["minwidth_metal3"] = 3*_lambda_
# 15.2 Minimum spacing to metal3
drc["metal3_to_metal3"] = 3*_lambda_
# 15.2 Minimum spacing to m3
drc.add_layer("m3",
width = 3*_lambda_,
spacing = 3*_lambda_)
# 15.3 Minimum overlap of via 2
drc["metal3_extend_via2"] = _lambda_
# Reserved for asymmetric enclosures
drc["metal3_enclosure_via2"] = _lambda_
# 21.3 Minimum overlap by metal3
drc["metal3_extend_via3"] = _lambda_
# Reserved for asymmetric enclosures
drc["metal3_enclosure_via3"] = _lambda_
# Not a rule
drc["minarea_metal3"] = 0
drc.add_enclosure("m3",
layer = "via2",
enclosure = _lambda_)
# 21.3 Minimum overlap by m3
drc.add_enclosure("m3",
layer = "via3",
enclosure = _lambda_)
# 21.1 Exact size
drc["minwidth_via3"] = 2*_lambda_
# 21.2 Minimum spacing
drc["via3_to_via3"] = 3*_lambda_
drc.add_layer("via3",
width = 2*_lambda_,
spacing = 3*_lambda_)
# 22.1 Minimum width
drc["minwidth_metal4"] = 6*_lambda_
# 22.2 Minimum spacing to metal4
drc["metal4_to_metal4"] = 6*_lambda_
# 22.2 Minimum spacing to m4
drc.add_layer("m4",
width = 6*_lambda_,
spacing = 6*_lambda_)
# 22.3 Minimum overlap of via 3
drc["metal4_extend_via3"] = 2*_lambda_
# Reserved for asymmetric enclosures
drc["metal4_enclosure_via3"] = 2*_lambda_
# Not a rule
drc["minarea_metal4"] = 0
drc.add_enclosure("m4",
layer = "via3",
enclosure = 2*_lambda_)
###################################################
##END DRC/LVS Rules
###################################################
###################################################
##Spice Simulation Parameters
# Spice Simulation Parameters
###################################################
# spice model info
@ -311,11 +370,7 @@ parameter["sa_inv_nmos_size"] = 9*_lambda_
parameter["bitcell_drain_cap"] = 0.2 #In Femto-Farad, approximation of drain capacitance
###################################################
##END Spice Simulation Parameters
###################################################
###################################################
##BEGIN Technology Tool Preferences
# Technology Tool Preferences
###################################################
drc_name = "magic"
@ -323,7 +378,3 @@ lvs_name = "netgen"
pex_name = "magic"
blackbox_bitcell = False
###################################################
##END Technology Tool Preferences
###################################################