This commit is contained in:
Matthew Guthaus 2019-12-23 21:16:08 +00:00
parent 4ad920eaf7
commit bec12f5b94
2 changed files with 395 additions and 329 deletions

View File

@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import itertools
import collections
import geometry
import gdsMill
@ -16,7 +15,7 @@ import os
from globals import OPTS
from vector import vector
from pin_layout import pin_layout
import lef
class layout():
"""
@ -61,12 +60,13 @@ class layout():
y_dir = 1
else:
# we lose a rail after every 2 gates
base_offset=vector(x_offset, (inv_num+1) * height - (inv_num%2)*drc["minwidth_m1"])
base_offset = vector(x_offset,
(inv_num + 1) * height - \
(inv_num % 2) * drc["minwidth_m1"])
y_dir = -1
return (base_offset, y_dir)
def find_lowest_coords(self):
"""Finds the lowest set of 2d cartesian coordinates within
this layout"""
@ -92,9 +92,10 @@ class layout():
return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2))
def find_highest_coords(self):
"""Finds the highest set of 2d cartesian coordinates within
this layout"""
"""
Finds the highest set of 2d cartesian coordinates within
this layout
"""
if len(self.objs) > 0:
highestx1 = max(obj.rx() for obj in self.objs if obj.name != "label")
highesty1 = max(obj.uy() for obj in self.objs if obj.name != "label")
@ -112,8 +113,8 @@ class layout():
elif highestx2 == None:
return vector(highestx1, highesty1)
else:
return vector(max(highestx1, highestx2), max(highesty1, highesty2))
return vector(max(highestx1, highestx2),
max(highesty1, highesty2))
def translate_all(self, offset):
"""
@ -164,7 +165,8 @@ class layout():
def add_rect_center(self, layer, offset, width=None, height=None):
"""
Adds a rectangle on a given layer at the center point with width and height
Adds a rectangle on a given layer at the center
point with width and height
"""
if not width:
width = drc["minwidth_{}".format(layer)]
@ -174,26 +176,33 @@ class layout():
lpp = techlayer[layer]
corrected_offset = offset - vector(0.5 * width, 0.5 * height)
if lpp[0] >= 0:
self.objs.append(geometry.rectangle(lpp, corrected_offset, width, height))
self.objs.append(geometry.rectangle(lpp,
corrected_offset,
width,
height))
return self.objs[-1]
return None
def add_segment_center(self, layer, start, end):
"""
Add a min-width rectanglular segment using center line on the start to end point
Add a min-width rectanglular segment using center
line on the start to end point
"""
minwidth_layer = drc["minwidth_{}".format(layer)]
if start.x != end.x and start.y != end.y:
debug.error("Nonrectilinear center rect!", -1)
elif start.x != end.x:
offset = vector(0, 0.5 * minwidth_layer)
return self.add_rect(layer,start-offset,end.x-start.x,minwidth_layer)
return self.add_rect(layer,
start-offset,
end.x-start.x,
minwidth_layer)
else:
offset = vector(0.5 * minwidth_layer, 0)
return self.add_rect(layer,start-offset,minwidth_layer,end.y-start.y)
return self.add_rect(layer,
start-offset,
minwidth_layer,
end.y-start.y)
def get_pin(self, text):
"""
@ -206,13 +215,10 @@ class layout():
# Otherwise, should use get_pins()
any_pin = next(iter(self.pin_map[text]))
return any_pin
except Exception as e:
#print e
except Exception:
self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1)
def get_pins(self, text):
"""
Return a pin list (instead of a single pin)
@ -235,12 +241,17 @@ class layout():
"""
pins = instance.get_pins(pin_name)
debug.check(len(pins)>0,"Could not find pin {}".format(pin_name))
debug.check(len(pins) > 0,
"Could not find pin {}".format(pin_name))
for pin in pins:
if new_name == "":
new_name = pin.name
self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height())
self.add_layout_pin(new_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
def copy_layout_pins(self, instance, prefix=""):
"""
@ -255,7 +266,8 @@ class layout():
Creates a path like pin with center-line convention
"""
debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.")
debug.check(start.x == end.x or start.y == end.y,
"Cannot have a non-manhatten layout pin.")
minwidth_layer = drc["minwidth_{}".format(layer)]
@ -273,8 +285,11 @@ class layout():
height = max(minwidth_layer, height)
width = max(minwidth_layer, width)
return self.add_layout_pin(text, layer, ll_offset, width, height)
return self.add_layout_pin(text,
layer,
ll_offset,
width,
height)
def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None):
""" Creates a path like pin with center-line convention """
@ -287,7 +302,6 @@ class layout():
return self.add_layout_pin(text, layer, ll_offset, width, height)
def remove_layout_pin(self, text):
"""
Delete a labeled pin (or all pins of the same name)
@ -303,7 +317,9 @@ class layout():
if not height:
height = drc["minwidth_{0}".format(layer)]
new_pin = pin_layout(text, [offset,offset+vector(width,height)], layer)
new_pin = pin_layout(text,
[offset, offset+vector(width, height)],
layer)
try:
# Check if there's a duplicate!
@ -333,8 +349,8 @@ class layout():
height=height)
self.add_label(text=text,
layer=layer,
offset=offset+vector(0.5*width,0.5*height))
offset=offset + vector(0.5 * width,
0.5 * height))
def add_label(self, text, layer, offset=[0, 0], zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level"""
@ -346,7 +362,6 @@ class layout():
return self.objs[-1]
return None
def add_path(self, layer, coordinates, width=None):
"""Connects a routing path on given layer,coordinates,width."""
debug.info(4, "add path " + str(layer) + " " + str(coordinates))
@ -376,12 +391,12 @@ class layout():
path=coordinates,
layer_widths=layer_widths)
def add_wire(self, layers, coordinates):
"""Connects a routing path on given layer,coordinates,width.
The layers are the (horizontal, via, vertical). """
import wire
# add an instance of our path that breaks down into rectangles and contacts
# add an instance of our path that breaks down
# into rectangles and contacts
wire.wire(obj=self,
layer_stack=layers,
position_list=coordinates)
@ -394,7 +409,7 @@ class layout():
def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """
if directions==None:
if not directions:
directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2]))
@ -414,9 +429,12 @@ class layout():
return inst
def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None):
""" Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """
"""
Add a three layer via structure by the center coordinate
accounting for mirroring and rotation.
"""
if directions==None:
if not directions:
directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2]))
@ -430,7 +448,8 @@ class layout():
height = via.height
width = via.width
corrected_offset = offset + vector(-0.5*width,-0.5*height)
corrected_offset = offset + vector(-0.5 * width,
-0.5 * height)
self.add_mod(via)
inst = self.add_inst(name=via.name,
@ -454,8 +473,6 @@ class layout():
rotate=rotate)
return inst
def gds_read(self):
"""Reads a GDSII file in the library and checks if it exists
Otherwise, start a new layout for dynamic generation."""
@ -480,7 +497,7 @@ class layout():
def print_gds(self, gds_file=None):
"""Print the gds file (not the vlsi class) to the terminal """
if gds_file == None:
if not gds_file:
gds_file = self.gds_file
debug.info(4, "Printing {}".format(gds_file))
arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"])
@ -511,10 +528,12 @@ class layout():
# If there is a boundary layer, and we didn't create one, add one.
if "stdc" in techlayer.keys():
boundary_layer = "stdc"
boundary = [self.find_lowest_coords(), self.find_highest_coords()]
boundary = [self.find_lowest_coords(),
self.find_highest_coords()]
height = boundary[1][1] - boundary[0][1]
width = boundary[1][0] - boundary[0][0]
(layer_number, layer_purpose) = techlayer[boundary_layer]
print(self.name, boundary, height, width)
gds_layout.addBox(layerNumber=layer_number,
purposeNumber=layer_purpose,
offsetInMicrons=boundary[0],
@ -523,7 +542,6 @@ class layout():
center=False)
debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary))
self.visited.append(self.name)
def gds_write(self, gds_name):
@ -559,7 +577,8 @@ class layout():
def get_blockages(self, layer, top_level=False):
"""
Write all of the obstacles in the current (and children) modules to the lef file
Write all of the obstacles in the current (and children)
modules to the lef file.
Do not write the pins since they aren't obstructions.
"""
if type(layer) == str:
@ -660,10 +679,12 @@ class layout():
offset=line_offset,
height=length)
# Make this the center of the rail
line_positions[names[i]]=line_offset+vector(half_minwidth,0.5*length)
line_positions[names[i]] = line_offset + vector(half_minwidth,
0.5 * length)
else:
for i in range(len(names)):
line_offset = offset + vector(0,i*pitch + half_minwidth)
line_offset = offset + vector(0,
i * pitch + half_minwidth)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
@ -674,7 +695,8 @@ class layout():
offset=line_offset,
width=length)
# Make this the center of the rail
line_positions[names[i]]=line_offset+vector(0.5*length,half_minwidth)
line_positions[names[i]] = line_offset + vector(0.5 * length,
half_minwidth)
return line_positions
@ -692,7 +714,8 @@ class layout():
"""
Connect a mapping of pin -> name for a bus. This could be
replaced with a channel router in the future.
NOTE: This has only really been tested with point-to-point connections (not multiple pins on a net).
NOTE: This has only really been tested with point-to-point
connections (not multiple pins on a net).
"""
(horizontal_layer, via_layer, vertical_layer) = layer_stack
if horizontal:
@ -712,16 +735,16 @@ class layout():
# left/right then up/down
mid_pos = vector(bus_pos.x, pin_pos.y)
self.add_wire(layer_stack,[bus_pos, mid_pos, pin_pos])
self.add_wire(layer_stack,
[bus_pos, mid_pos, pin_pos])
# Connect to the pin on the instances with a via if it is
# not on the right layer
if pin.layer != route_layer:
self.add_via_center(layers=layer_stack,
offset=pin_pos)
# FIXME: output pins tend to not be rotate, but supply pins are. Make consistent?
# FIXME: output pins tend to not be rotate,
# but supply pins are. Make consistent?
# We only need a via if they happened to align perfectly
# so the add_wire didn't add a via
@ -729,20 +752,31 @@ class layout():
self.add_via_center(layers=layer_stack,
offset=bus_pos,
rotate=90)
def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """
if layer == "m1":
return (self.m1_pitch,self.m1_pitch-self.m1_space,self.m1_space)
return (self.m1_pitch,
self.m1_pitch - self.m1_space,
self.m1_space)
elif layer == "m2":
return (self.m2_pitch,self.m2_pitch-self.m2_space,self.m2_space)
return (self.m2_pitch,
self.m2_pitch - self.m2_space,
self.m2_space)
elif layer == "m3":
return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space)
return (self.m3_pitch,
self.m3_pitch - self.m3_space,
self.m3_space)
elif layer == "m4":
from tech import layer as tech_layer
if "m4" in tech_layer:
return (self.m3_pitch,self.m3_pitch-self.m4_space,self.m4_space)
return (self.m3_pitch,
self.m3_pitch - self.m4_space,
self.m4_space)
else:
return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space)
return (self.m3_pitch,
self.m3_pitch - self.m3_space,
self.m3_space)
else:
debug.error("Cannot find layer pitch.")
@ -752,18 +786,20 @@ class layout():
layer_stack,
pitch):
"""
Create a trunk route for all pins with the trunk located at the given y offset.
Create a trunk route for all pins with
the trunk located at the given y offset.
"""
max_x = max([pin.center().x for pin in pins])
min_x = min([pin.center().x for pin in pins])
# if we are less than a pitch, just create a non-preferred layer jog
if max_x-min_x <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
# Add the horizontal trunk on the vertical layer!
self.add_path(self.vertical_layer,[vector(min_x-half_layer_width,trunk_offset.y), vector(max_x+half_layer_width,trunk_offset.y)])
self.add_path(self.vertical_layer,
[vector(min_x - half_layer_width, trunk_offset.y),
vector(max_x + half_layer_width, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
@ -772,8 +808,9 @@ class layout():
self.add_path(self.vertical_layer, [pin.center(), mid])
else:
# Add the horizontal trunk
self.add_path(self.horizontal_layer,[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)])
trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y)
self.add_path(self.horizontal_layer,
[vector(min_x, trunk_offset.y),
vector(max_x, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
@ -788,7 +825,8 @@ class layout():
layer_stack,
pitch):
"""
Create a trunk route for all pins with the trunk located at the given x offset.
Create a trunk route for all pins with the
trunk located at the given x offset.
"""
max_y = max([pin.center().y for pin in pins])
min_y = min([pin.center().y for pin in pins])
@ -799,7 +837,9 @@ class layout():
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
# Add the vertical trunk on the horizontal layer!
self.add_path(self.horizontal_layer,[vector(trunk_offset.x,min_y-half_layer_width), vector(trunk_offset.x,max_y+half_layer_width)])
self.add_path(self.horizontal_layer,
[vector(trunk_offset.x, min_y - half_layer_width),
vector(trunk_offset.x,max_y + half_layer_width)])
# Route each pin to the trunk
for pin in pins:
@ -808,8 +848,9 @@ class layout():
self.add_path(self.horizontal_layer, [pin.center(), mid])
else:
# Add the vertical trunk
self.add_path(self.vertical_layer,[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)])
trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),)
self.add_path(self.vertical_layer,
[vector(trunk_offset.x, min_y),
vector(trunk_offset.x, max_y)])
# Route each pin to the trunk
for pin in pins:
@ -818,7 +859,6 @@ class layout():
self.add_via_center(layers=layer_stack,
offset=mid)
def create_channel_route(self, netlist,
offset,
layer_stack,
@ -848,7 +888,7 @@ class layout():
def vcg_nets_overlap(net1, net2, vertical, pitch):
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps
overlap if any pin overlaps.
"""
for pin1 in net1:
@ -861,7 +901,8 @@ class layout():
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin, for example, so the
# However, a top pin shouldn't overlap another top pin,
# for example, so the
# extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
@ -879,15 +920,18 @@ class layout():
self.vertical_layer = layer_stack[2]
self.horizontal_layer = layer_stack[0]
(self.vertical_pitch,self.vertical_width,self.vertical_space) = self.get_layer_pitch(self.vertical_layer)
(self.horizontal_pitch,self.horizontal_width,self.horizontal_space) = self.get_layer_pitch(self.horizontal_layer)
layer_stuff = self.get_layer_pitch(self.vertical_layer)
(self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
(self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
# FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the
# FIXME: Must extend this to a horizontal conflict graph
# too if we want to minimize the
# number of tracks!
# hcg = {}
# Initialize the vertical conflict graph (vcg) and make a list of all pins
# Initialize the vertical conflict graph (vcg)
# and make a list of all pins
vcg = collections.OrderedDict()
# Create names for the nets for the graphs
@ -910,9 +954,15 @@ class layout():
# Skip yourself
if net_name1 == net_name2:
continue
if vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.vertical_pitch):
if vertical and vcg_nets_overlap(nets[net_name1],
nets[net_name2],
vertical,
self.vertical_pitch):
vcg[net_name2].append(net_name1)
elif not vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.horizontal_pitch):
elif not vertical and vcg_nets_overlap(nets[net_name1],
nets[net_name2],
vertical,
self.horizontal_pitch):
vcg[net_name2].append(net_name1)
# list of routes to do
@ -929,8 +979,6 @@ class layout():
# FIXME: We don't support cyclic VCGs right now.
debug.error("Cyclic VCG in channel router.", -1)
# These are the pins we'll have to connect
pin_list = nets[net_name]
# print("Routing:", net_name, [x.name for x in pin_list])
@ -938,15 +986,21 @@ class layout():
# Remove the net from other constriants in the VCG
vcg = remove_net_from_graph(net_name, vcg)
# Add the trunk routes from the bottom up for horizontal or the left to right for vertical
# Add the trunk routes from the bottom up for
# horizontal or the left to right for vertical
if vertical:
self.add_vertical_trunk_route(pin_list, offset, layer_stack, self.vertical_pitch)
self.add_vertical_trunk_route(pin_list,
offset,
layer_stack,
self.vertical_pitch)
offset += vector(self.vertical_pitch, 0)
else:
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, self.horizontal_pitch)
self.add_horizontal_trunk_route(pin_list,
offset,
layer_stack,
self.horizontal_pitch)
offset += vector(0, self.horizontal_pitch)
def create_vertical_channel_route(self, netlist, offset, layer_stack):
"""
Wrapper to create a vertical channel route
@ -996,15 +1050,19 @@ class layout():
width=xmax-xmin,
height=ymax-ymin)
def copy_power_pins(self, inst, name):
"""
This will copy a power pin if it is on M3. If it is on M1, it will add a power via too.
This will copy a power pin if it is on M3.
If it is on M1, it will add a power via too.
"""
pins = inst.get_pins(name)
for pin in pins:
if pin.layer == "m3":
self.add_layout_pin(name, pin.layer, pin.ll(), pin.width(), pin.height())
self.add_layout_pin(name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
elif pin.layer == "m1":
self.add_power_pin(name, pin.center())
else:
@ -1028,13 +1086,11 @@ class layout():
offset=loc,
directions=direction)
if start_layer == "m1" or start_layer == "m2":
via = self.add_via_center(layers=self.m2_stack,
size=size,
offset=loc,
directions=direction)
if start_layer == "m3":
self.add_layout_pin_rect_center(text=name,
layer="m3",
@ -1048,10 +1104,11 @@ class layout():
def add_power_ring(self, bbox):
"""
Create vdd and gnd power rings around an area of the bounding box argument. Must
have a supply_rail_width and supply_rail_pitch defined as a member variable.
Defines local variables of the left/right/top/bottom vdd/gnd center offsets
for use in other modules..
Create vdd and gnd power rings around an area of the bounding box
argument. Must have a supply_rail_width and supply_rail_pitch
defined as a member variable. Defines local variables of the
left/right/top/bottom vdd/gnd center offsets for use in other
modules..
"""
[ll, ur] = bbox
@ -1061,15 +1118,16 @@ class layout():
width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails
offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch)
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
left_gnd_pin = self.add_layout_pin(text="gnd",
layer="m2",
offset=offset,
width=self.supply_rail_width,
height=height)
offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
left_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2",
offset=offset,
@ -1084,7 +1142,8 @@ class layout():
width=self.supply_rail_width,
height=height)
offset = vector(ur.x,ll.y) + vector(self.supply_rail_pitch,-1*self.supply_rail_pitch)
offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
right_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2",
offset=offset,
@ -1092,14 +1151,16 @@ class layout():
height=height)
# BOTTOM horizontal rails
offset = ll + vector(-2*self.supply_rail_pitch, -2*self.supply_rail_pitch)
offset = ll + vector(-2 * self.supply_rail_pitch,
-2 * self.supply_rail_pitch)
bottom_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch)
offset = ll + vector(-1 * self.supply_rail_pitch,
-1 * self.supply_rail_pitch)
bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1",
offset=offset,
@ -1107,14 +1168,16 @@ class layout():
height=self.supply_rail_width)
# TOP horizontal rails
offset = vector(ll.x, ur.y) + vector(-2*self.supply_rail_pitch,0)
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
0)
top_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1",
offset=offset,
width=width,
height=self.supply_rail_width)
offset = vector(ll.x, ur.y) + vector(-1*self.supply_rail_pitch, self.supply_rail_pitch)
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
self.supply_rail_pitch)
top_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1",
offset=offset,
@ -1132,7 +1195,6 @@ class layout():
self.top_gnd_y_center = top_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy()
# Find the number of vias for this pitch
self.supply_vias = 1
from sram_factory import factory
@ -1158,12 +1220,14 @@ class layout():
for pt in via_points:
self.add_via_center(layers=self.m1_stack,
offset=pt,
size = (self.supply_vias, self.supply_vias))
size=(self.supply_vias,
self.supply_vias))
def pdf_write(self, pdf_name):
# NOTE: Currently does not work (Needs further research)
"""
Display the layout to a PDF file.
"""
debug.error("NOTE: Currently does not work (Needs further research)")
# self.pdf_name = self.name + ".pdf"
debug.info(0, "Writing to {}".format(pdf_name))
pdf = gdsMill.pdfLayout(self.gds)

View File

@ -108,7 +108,7 @@ class ptx(design.design):
Pre-compute some handy layout parameters.
"""
if self.num_contacts==None:
if not self.num_contacts:
self.num_contacts = self.calculate_num_contacts()
# Determine layer types needed
@ -121,35 +121,36 @@ class ptx(design.design):
else:
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=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.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.contact_to_gate + self.contact_width + self.poly_width
self.contact_pitch = 2 * self.contact_to_gate + \
self.contact_width + self.poly_width
# The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(self.active_enclose_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.contact_to_gate
# 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.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.ptx_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
self.ptx_active_height = self.tx_width
# Poly height must include poly extension over active
self.poly_height = self.tx_width + 2 * self.poly_extend_active
@ -177,11 +178,11 @@ class ptx(design.design):
# 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,
0.5 * self.active_height)
0.5 * self.ptx_active_height)
# Min area results are just flagged for now.
debug.check(self.active_width * self.active_height >= self.minarea_active,
debug.check(self.ptx_active_width * self.ptx_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.
@ -215,14 +216,14 @@ 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=self.poly_width)
def connect_fingered_active(self, drain_positions, source_positions):
"""
Connect each contact up/down to a source or drain pin
@ -230,10 +231,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 \
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
@ -247,7 +248,8 @@ class ptx(design.design):
if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir, source_dir)
self.remove_layout_pin("S") # remove the individual connections
# remove the individual connections
self.remove_layout_pin("S")
# Add each vertical segment
for a in source_positions:
self.add_path(("m1"),
@ -310,16 +312,16 @@ class ptx(design.design):
"""
self.add_rect(layer="active",
offset=self.active_offset,
width=self.active_width,
height=self.active_height)
width=self.ptx_active_width,
height=self.ptx_active_height)
# If the implant must enclose the active, shift offset
# and increase width/height
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,
width=self.active_width + 2 * enclose_width,
height=self.active_height + 2 * enclose_width)
width=self.ptx_active_width + 2 * enclose_width,
height=self.ptx_active_height + 2 * enclose_width)
def add_well_implant(self):
"""