mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into bisr
This commit is contained in:
commit
34939ebd70
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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. """
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
###################################################
|
||||
|
|
|
|||
|
|
@ -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={}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
###################################################
|
||||
|
|
|
|||
Loading…
Reference in New Issue