2018-07-10 00:42:46 +02:00
|
|
|
import hierarchy_design
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
2017-12-13 00:50:45 +01:00
|
|
|
import utils
|
2016-11-08 18:57:35 +01:00
|
|
|
from tech import drc
|
|
|
|
|
from vector import vector
|
|
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2018-07-10 00:42:46 +02:00
|
|
|
class contact(hierarchy_design.hierarchy_design):
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2017-04-26 19:24:51 +02:00
|
|
|
Object for a contact shape with its conductor enclosures.
|
2016-11-08 18:57:35 +01:00
|
|
|
Creates a contact array minimum active or poly enclosure and metal1 enclosure.
|
|
|
|
|
This class has enclosure on multiple sides of the contact whereas a via may
|
|
|
|
|
have extension on two or four sides.
|
2018-01-11 19:24:44 +01:00
|
|
|
The well/implant_type is an option to add a select/implant layer enclosing the contact. This is
|
|
|
|
|
necessary to import layouts into Magic which requires the select to be in the same GDS
|
|
|
|
|
hierarchy as the contact.
|
2016-11-08 18:57:35 +01:00
|
|
|
"""
|
2018-09-05 01:35:40 +02:00
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
def __init__(self, layer_stack, dimensions=[1,1], implant_type=None, well_type=None):
|
|
|
|
|
if implant_type or well_type:
|
2018-09-05 01:40:52 +02:00
|
|
|
name = "{0}_{1}_{2}_{3}x{4}_{5}{6}".format(layer_stack[0],
|
|
|
|
|
layer_stack[1],
|
|
|
|
|
layer_stack[2],
|
|
|
|
|
dimensions[0],
|
|
|
|
|
dimensions[1],
|
|
|
|
|
implant_type,
|
|
|
|
|
well_type)
|
|
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
else:
|
2018-09-05 01:40:52 +02:00
|
|
|
name = "{0}_{1}_{2}_{3}x{4}".format(layer_stack[0],
|
|
|
|
|
layer_stack[1],
|
|
|
|
|
layer_stack[2],
|
|
|
|
|
dimensions[0],
|
|
|
|
|
dimensions[1])
|
|
|
|
|
|
2018-07-10 00:42:46 +02:00
|
|
|
hierarchy_design.hierarchy_design.__init__(self, name)
|
2017-09-30 01:22:13 +02:00
|
|
|
debug.info(4, "create contact object {0}".format(name))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
self.layer_stack = layer_stack
|
|
|
|
|
self.dimensions = dimensions
|
2017-04-26 19:24:51 +02:00
|
|
|
self.offset = vector(0,0)
|
2018-01-11 19:24:44 +01:00
|
|
|
self.implant_type = implant_type
|
|
|
|
|
self.well_type = well_type
|
2016-11-08 18:57:35 +01:00
|
|
|
self.pins = [] # used for matching parm lengths
|
|
|
|
|
self.create_layout()
|
|
|
|
|
|
|
|
|
|
def create_layout(self):
|
|
|
|
|
self.setup_layers()
|
|
|
|
|
self.setup_layout_constants()
|
|
|
|
|
self.create_contact_array()
|
|
|
|
|
self.create_first_layer_enclosure()
|
|
|
|
|
self.create_second_layer_enclosure()
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-11 02:28:06 +01:00
|
|
|
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)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
# 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.")
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def setup_layers(self):
|
|
|
|
|
(first_layer, via_layer, second_layer) = self.layer_stack
|
|
|
|
|
self.first_layer_name = first_layer
|
|
|
|
|
self.via_layer_name = via_layer
|
2018-01-11 19:24:44 +01:00
|
|
|
# Some technologies have a separate active contact from the poly contact
|
|
|
|
|
# We will use contact for DRC, but active_contact for output
|
|
|
|
|
if first_layer=="active" or second_layer=="active":
|
|
|
|
|
self.via_layer_name_expanded = "active_"+via_layer
|
|
|
|
|
else:
|
|
|
|
|
self.via_layer_name_expanded = via_layer
|
2016-11-08 18:57:35 +01:00
|
|
|
self.second_layer_name = second_layer
|
|
|
|
|
|
|
|
|
|
def setup_layout_constants(self):
|
2018-10-12 23:37:51 +02:00
|
|
|
self.contact_width = drc("minwidth_{0}". format(self.via_layer_name))
|
|
|
|
|
contact_to_contact = drc("{0}_to_{0}".format(self.via_layer_name))
|
2018-01-11 19:24:44 +01:00
|
|
|
self.contact_pitch = self.contact_width + contact_to_contact
|
2017-08-24 00:02:15 +02:00
|
|
|
self.contact_array_width = self.contact_width + (self.dimensions[0] - 1) * self.contact_pitch
|
|
|
|
|
self.contact_array_height = self.contact_width + (self.dimensions[1] - 1) * self.contact_pitch
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-01-11 19:24:44 +01:00
|
|
|
# DRC rules
|
2018-10-12 23:37:51 +02:00
|
|
|
first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
|
|
|
|
|
first_layer_minarea = drc("minarea_{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))
|
|
|
|
|
second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
|
|
|
|
|
second_layer_minarea = drc("minarea_{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))
|
2018-01-11 19:24:44 +01:00
|
|
|
|
|
|
|
|
self.first_layer_horizontal_enclosure = max((first_layer_minwidth - self.contact_array_width) / 2,
|
|
|
|
|
first_layer_enclosure)
|
|
|
|
|
self.first_layer_vertical_enclosure = max(utils.ceil((first_layer_minarea
|
|
|
|
|
/ (self.contact_array_width + 2*self.first_layer_horizontal_enclosure)
|
|
|
|
|
- self.contact_array_height)/2),
|
|
|
|
|
(first_layer_minwidth - self.contact_array_height)/2,
|
|
|
|
|
first_layer_extend)
|
|
|
|
|
|
|
|
|
|
self.second_layer_horizontal_enclosure = max((second_layer_minwidth - self.contact_array_width)/2,
|
|
|
|
|
second_layer_enclosure)
|
|
|
|
|
self.second_layer_vertical_enclosure = max(utils.ceil((second_layer_minarea
|
|
|
|
|
/ (self.contact_array_width + 2*self.second_layer_horizontal_enclosure)
|
|
|
|
|
- self.contact_array_height)/2),
|
|
|
|
|
(second_layer_minwidth - self.contact_array_height)/2,
|
|
|
|
|
second_layer_extend)
|
|
|
|
|
|
|
|
|
|
def create_contact_array(self):
|
|
|
|
|
""" Create the contact array at the origin"""
|
2017-08-24 00:02:15 +02:00
|
|
|
# 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))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
for i in range(self.dimensions[1]):
|
2017-08-24 00:02:15 +02:00
|
|
|
offset = self.via_layer_position + vector(0, self.contact_pitch * i)
|
2016-11-08 18:57:35 +01:00
|
|
|
for j in range(self.dimensions[0]):
|
2018-01-11 19:24:44 +01:00
|
|
|
self.add_rect(layer=self.via_layer_name_expanded,
|
2016-11-08 18:57:35 +01:00
|
|
|
offset=offset,
|
|
|
|
|
width=self.contact_width,
|
|
|
|
|
height=self.contact_width)
|
2017-08-24 00:02:15 +02:00
|
|
|
offset = offset + vector(self.contact_pitch,0)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_first_layer_enclosure(self):
|
2018-01-11 19:24:44 +01:00
|
|
|
# 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
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer=self.first_layer_name,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=self.first_layer_position,
|
2018-01-11 19:24:44 +01:00
|
|
|
width=self.first_layer_width,
|
|
|
|
|
height=self.first_layer_height)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_second_layer_enclosure(self):
|
2018-01-11 19:24:44 +01:00
|
|
|
# this is if the first and second layers are different
|
|
|
|
|
self.second_layer_position = vector(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
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_rect(layer=self.second_layer_name,
|
2017-08-24 00:02:15 +02:00
|
|
|
offset=self.second_layer_position,
|
2018-01-11 19:24:44 +01:00
|
|
|
width=self.second_layer_width,
|
|
|
|
|
height=self.second_layer_height)
|
|
|
|
|
|
|
|
|
|
def create_implant_well_enclosures(self):
|
2018-10-12 23:37:51 +02:00
|
|
|
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")
|
2018-01-11 19:24:44 +01:00
|
|
|
self.add_rect(layer="{}implant".format(self.implant_type),
|
|
|
|
|
offset=implant_position,
|
|
|
|
|
width=implant_width,
|
|
|
|
|
height=implant_height)
|
2018-10-12 23:37:51 +02:00
|
|
|
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")
|
2018-01-11 19:24:44 +01:00
|
|
|
self.add_rect(layer="{}well".format(self.well_type),
|
|
|
|
|
offset=well_position,
|
|
|
|
|
width=well_width,
|
|
|
|
|
height=well_height)
|
|
|
|
|
|
2018-07-10 19:17:23 +02:00
|
|
|
def analytical_power(self, proc, vdd, temp, load):
|
|
|
|
|
""" Get total power of a module """
|
|
|
|
|
return self.return_power()
|
2017-12-12 23:53:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# This is not instantiated and used for calculations only.
|
|
|
|
|
# These are static 1x1 contacts to reuse in all the design modules.
|
|
|
|
|
well = contact(layer_stack=("active", "contact", "metal1"))
|
|
|
|
|
active = contact(layer_stack=("active", "contact", "poly"))
|
|
|
|
|
poly = contact(layer_stack=("poly", "contact", "metal1"))
|
|
|
|
|
m1m2 = contact(layer_stack=("metal1", "via1", "metal2"))
|
|
|
|
|
m2m3 = contact(layer_stack=("metal2", "via2", "metal3"))
|
2018-07-10 00:42:46 +02:00
|
|
|
#m3m4 = contact(layer_stack=("metal3", "via3", "metal4"))
|
2017-12-12 23:53:19 +01:00
|
|
|
|