Cleanup and simplify ptx for multiple technologies

This commit is contained in:
mrg 2020-02-25 00:36:22 +00:00
parent 585a708e0c
commit 254e584e35
1 changed files with 84 additions and 69 deletions

View File

@ -10,6 +10,7 @@ import debug
from tech import layer, drc, spice
from vector import vector
from sram_factory import factory
import contact
class ptx(design.design):
@ -28,6 +29,7 @@ class ptx(design.design):
tx_type="nmos",
connect_active=False,
connect_poly=False,
series_devices=False,
num_contacts=None):
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
@ -50,6 +52,7 @@ class ptx(design.design):
self.tx_width = width
self.connect_active = connect_active
self.connect_poly = connect_poly
self.series_devices = series_devices
self.num_contacts = num_contacts
# Since it has variable height, it is not a pgate.
@ -127,27 +130,25 @@ class ptx(design.design):
directions=("V", "V"),
dimensions=(1, self.num_contacts))
# 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
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 extra poly spacing due to the poly contact to poly contact pitch
# of contacted gates
extra_poly_contact_width = contact.poly_contact.width - self.poly_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
# This is the spacing between S/D contacts
# This is the spacing between the poly gates
self.min_poly_pitch = self.poly_space + self.poly_width
self.contacted_poly_pitch = self.poly_space + contact.poly_contact.width
self.contact_pitch = 2 * self.contact_to_gate + self.poly_width + self.contact_width
self.poly_pitch = max(self.min_poly_pitch,
self.contacted_poly_pitch,
self.contact_pitch)
self.end_to_contact = 0.5 * self.active_contact.width
# 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_contact + self.active_contact.width \
+ 2 * self.contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
# Active height is just the transistor width
self.active_height = self.tx_width
@ -287,12 +288,11 @@ class ptx(design.design):
Add the poly gates(s) and (optionally) connect them.
"""
# poly is one contacted spacing from the end and down an extension
poly_offset = self.active_offset \
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \
+ vector(self.end_to_poly, -self.poly_extend_active)
poly_offset = self.contact_offset \
+ vector(0.5 * self.active_contact.width + 0.5 * self.poly_width + self.contact_to_gate, 0)
# poly_positions are the bottom center of the poly gates
poly_positions = []
self.poly_positions = []
# It is important that these are from left to right,
# so that the pins are in the right
@ -309,11 +309,11 @@ class ptx(design.design):
offset=poly_offset,
height=self.poly_height,
width=self.poly_width)
poly_positions.append(poly_offset)
self.poly_positions.append(poly_offset)
poly_offset = poly_offset + vector(self.poly_pitch,0)
if self.connect_poly:
self.connect_fingered_poly(poly_positions)
self.connect_fingered_poly(self.poly_positions)
def add_active(self):
"""
@ -362,59 +362,74 @@ class ptx(design.design):
"""
return 1
def get_contact_positions(self):
"""
Create a list of the centers of drain and source contact positions.
"""
# The first one will always be a source
source_positions = [self.contact_offset]
drain_positions = []
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors.
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_spacing, 0))
else:
# It's a drain... so offset from previous source.
drain_positions.append(source_positions[-1] + vector(self.contact_spacing, 0))
return [source_positions,drain_positions]
def add_active_contacts(self):
"""
Add the active contacts to the transistor.
"""
drain_positions = []
source_positions = []
[source_positions,drain_positions] = self.get_contact_positions()
for pos in source_positions:
contact=self.add_via_center(layers=self.active_stack,
# First one is always a SOURCE
label = "S"
pos = self.contact_offset
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="S",
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
source_positions.append(pos)
for pos in drain_positions:
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text="D",
layer="m1",
# Skip these if they are going to be in series
if not self.series_devices:
for (poly1, poly2) in zip(self.poly_positions, self.poly_positions[1:]):
pos = vector(0.5 * (poly1.x + poly2.x),
self.contact_offset.y)
# Alternate source and drains
if label == "S":
label = "D"
drain_positions.append(pos)
else:
label = "S"
source_positions.append(pos)
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
pos = vector(self.active_offset.x + self.active_width - 0.5 * self.active_contact.width,
self.contact_offset.y)
# Last one is the opposite of previous
if label == "S":
label = "D"
drain_positions.append(pos)
else:
label = "S"
source_positions.append(pos)
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V","V"),
implant_type=self.implant_type,
well_type=self.well_type)
self.add_layout_pin_rect_center(text=label,
layer="m1",
offset=pos,
width=contact.mod.second_layer_width,
height=contact.mod.second_layer_height)
if self.connect_active:
self.connect_fingered_active(drain_positions, source_positions)