Merge branch 'tech_migration' into dev

This commit is contained in:
Matt Guthaus 2020-01-25 12:03:56 -08:00
commit e62beae805
21 changed files with 642 additions and 468 deletions

View File

@ -65,8 +65,10 @@ class contact(hierarchy_design.hierarchy_design):
self.create_second_layer_enclosure() self.create_second_layer_enclosure()
self.create_nitride_cut_enclosure() self.create_nitride_cut_enclosure()
self.height = max(obj.offset.y + obj.height for obj in self.objs) self.height = max(self.first_layer_position.y + self.first_layer_height,
self.width = max(obj.offset.x + obj.width for obj in self.objs) 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 # Do not include the select layer in the height/width
if self.implant_type and self.well_type: if self.implant_type and self.well_type:
@ -83,7 +85,7 @@ class contact(hierarchy_design.hierarchy_design):
self.second_layer_name = second_layer self.second_layer_name = second_layer
# Contacts will have unique per first layer # Contacts will have unique per first layer
if via_layer in tech.layer.keys(): if via_layer in tech.layer:
self.via_layer_name = via_layer self.via_layer_name = via_layer
elif via_layer == "contact": elif via_layer == "contact":
if first_layer in ("active", "poly"): if first_layer in ("active", "poly"):
@ -171,7 +173,7 @@ class contact(hierarchy_design.hierarchy_design):
def create_nitride_cut_enclosure(self): def create_nitride_cut_enclosure(self):
""" Special layer that encloses poly contacts in some processes """ """ Special layer that encloses poly contacts in some processes """
# Check if there is a special poly nitride cut layer # Check if there is a special poly nitride cut layer
if "npc" not in tech.layer.keys(): if "npc" not in tech.layer:
return return
# Only add for poly layers # Only add for poly layers
@ -224,10 +226,15 @@ class contact(hierarchy_design.hierarchy_design):
offset=implant_position, offset=implant_position,
width=implant_width, width=implant_width,
height=implant_height) height=implant_height)
well_position = self.first_layer_position - [drc("well_enclose_active")] * 2
well_width = self.first_layer_width + 2 * drc("well_enclose_active") # Optionally implant well if layer exists
well_height = self.first_layer_height + 2 * drc("well_enclose_active") well_layer = "{}well".format(self.well_type)
self.add_rect(layer="{}well".format(self.well_type), if well_layer in tech.layer:
well_enclose_active = drc(well_layer + "_enclose_active")
well_position = self.first_layer_position - [well_enclose_active] * 2
well_width = self.first_layer_width + 2 * well_enclose_active
well_height = self.first_layer_height + 2 * well_enclose_active
self.add_rect(layer=well_layer,
offset=well_position, offset=well_position,
width=well_width, width=well_width,
height=well_height) height=well_height)

View File

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

View File

@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import itertools
import collections import collections
import geometry import geometry
import gdsMill import gdsMill
@ -16,7 +15,7 @@ import os
from globals import OPTS from globals import OPTS
from vector import vector from vector import vector
from pin_layout import pin_layout from pin_layout import pin_layout
import lef
class layout(): class layout():
""" """
@ -32,7 +31,7 @@ class layout():
self.name = name self.name = name
self.width = None self.width = None
self.height = None self.height = None
self.boundary = None self.bounding_box = None
self.insts = [] # Holds module/cell layout instances self.insts = [] # Holds module/cell layout instances
self.objs = [] # Holds all other objects (labels, geometries, etc) self.objs = [] # Holds all other objects (labels, geometries, etc)
self.pin_map = {} # Holds name->pin_layout map for all pins self.pin_map = {} # Holds name->pin_layout map for all pins
@ -61,15 +60,18 @@ class layout():
y_dir = 1 y_dir = 1
else: else:
# we lose a rail after every 2 gates # 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 y_dir = -1
return (base_offset, y_dir) return (base_offset, y_dir)
def find_lowest_coords(self): def find_lowest_coords(self):
"""Finds the lowest set of 2d cartesian coordinates within """
this layout""" Finds the lowest set of 2d cartesian coordinates within
this layout
"""
if len(self.objs) > 0: if len(self.objs) > 0:
lowestx1 = min(obj.lx() for obj in self.objs if obj.name != "label") lowestx1 = min(obj.lx() for obj in self.objs if obj.name != "label")
@ -92,9 +94,10 @@ class layout():
return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2)) return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2))
def find_highest_coords(self): 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: if len(self.objs) > 0:
highestx1 = max(obj.rx() for obj in self.objs if obj.name != "label") 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") highesty1 = max(obj.uy() for obj in self.objs if obj.name != "label")
@ -112,8 +115,56 @@ class layout():
elif highestx2 == None: elif highestx2 == None:
return vector(highestx1, highesty1) return vector(highestx1, highesty1)
else: else:
return vector(max(highestx1, highestx2), max(highesty1, highesty2)) return vector(max(highestx1, highestx2),
max(highesty1, highesty2))
def find_highest_layer_coords(self, layer):
"""
Finds the highest set of 2d cartesian coordinates within
this layout on a layer
"""
# Only consider the layer not the purpose for now
layerNumber = techlayer[layer][0]
try:
highestx = max(obj.rx() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
highestx =0
try:
highesty = max(obj.uy() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
highesty = 0
for inst in self.insts:
# This really should be rotated/mirrored etc...
subcoord = inst.mod.find_highest_layer_coords(layer) + inst.offset
highestx = max(highestx, subcoord.x)
highesty = max(highesty, subcoord.y)
return vector(highestx, highesty)
def find_lowest_layer_coords(self, layer):
"""
Finds the highest set of 2d cartesian coordinates within
this layout on a layer
"""
# Only consider the layer not the purpose for now
layerNumber = techlayer[layer][0]
try:
lowestx = min(obj.lx() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
lowestx = 0
try:
lowesty = min(obj.by() for obj in self.objs if obj.layerNumber == layerNumber)
except ValueError:
lowesty = 0
for inst in self.insts:
# This really should be rotated/mirrored etc...
subcoord = inst.mod.find_lowest_layer_coords(layer) + inst.offset
lowestx = min(lowestx, subcoord.x)
lowesty = min(lowesty, subcoord.y)
return vector(lowestx, lowesty)
def translate_all(self, offset): def translate_all(self, offset):
""" """
@ -164,7 +215,8 @@ class layout():
def add_rect_center(self, layer, offset, width=None, height=None): 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: if not width:
width = drc["minwidth_{}".format(layer)] width = drc["minwidth_{}".format(layer)]
@ -174,26 +226,33 @@ class layout():
lpp = techlayer[layer] lpp = techlayer[layer]
corrected_offset = offset - vector(0.5 * width, 0.5 * height) corrected_offset = offset - vector(0.5 * width, 0.5 * height)
if lpp[0] >= 0: 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 self.objs[-1]
return None return None
def add_segment_center(self, layer, start, end): 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)] minwidth_layer = drc["minwidth_{}".format(layer)]
if start.x != end.x and start.y != end.y: if start.x != end.x and start.y != end.y:
debug.error("Nonrectilinear center rect!", -1) debug.error("Nonrectilinear center rect!", -1)
elif start.x != end.x: elif start.x != end.x:
offset = vector(0, 0.5 * minwidth_layer) 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: else:
offset = vector(0.5 * minwidth_layer, 0) 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): def get_pin(self, text):
""" """
@ -206,13 +265,10 @@ class layout():
# Otherwise, should use get_pins() # Otherwise, should use get_pins()
any_pin = next(iter(self.pin_map[text])) any_pin = next(iter(self.pin_map[text]))
return any_pin return any_pin
except Exception as e: except Exception:
#print e
self.gds_write("missing_pin.gds") 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) 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): def get_pins(self, text):
""" """
Return a pin list (instead of a single pin) Return a pin list (instead of a single pin)
@ -235,12 +291,17 @@ class layout():
""" """
pins = instance.get_pins(pin_name) 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: for pin in pins:
if new_name == "": if new_name == "":
new_name = pin.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=""): def copy_layout_pins(self, instance, prefix=""):
""" """
@ -255,7 +316,8 @@ class layout():
Creates a path like pin with center-line convention 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)] minwidth_layer = drc["minwidth_{}".format(layer)]
@ -273,8 +335,11 @@ class layout():
height = max(minwidth_layer, height) height = max(minwidth_layer, height)
width = max(minwidth_layer, width) width = max(minwidth_layer, width)
return self.add_layout_pin(text,
return self.add_layout_pin(text, layer, ll_offset, width, height) layer,
ll_offset,
width,
height)
def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None):
""" Creates a path like pin with center-line convention """ """ Creates a path like pin with center-line convention """
@ -287,7 +352,6 @@ class layout():
return self.add_layout_pin(text, layer, ll_offset, width, height) return self.add_layout_pin(text, layer, ll_offset, width, height)
def remove_layout_pin(self, text): def remove_layout_pin(self, text):
""" """
Delete a labeled pin (or all pins of the same name) Delete a labeled pin (or all pins of the same name)
@ -303,7 +367,9 @@ class layout():
if not height: if not height:
height = drc["minwidth_{0}".format(layer)] 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: try:
# Check if there's a duplicate! # Check if there's a duplicate!
@ -333,8 +399,8 @@ class layout():
height=height) height=height)
self.add_label(text=text, self.add_label(text=text,
layer=layer, 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): def add_label(self, text, layer, offset=[0, 0], zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level""" """Adds a text label on the given layer,offset, and zoom level"""
@ -346,7 +412,6 @@ class layout():
return self.objs[-1] return self.objs[-1]
return None return None
def add_path(self, layer, coordinates, width=None): def add_path(self, layer, coordinates, width=None):
"""Connects a routing path on given layer,coordinates,width.""" """Connects a routing path on given layer,coordinates,width."""
debug.info(4, "add path " + str(layer) + " " + str(coordinates)) debug.info(4, "add path " + str(layer) + " " + str(coordinates))
@ -376,12 +441,12 @@ class layout():
path=coordinates, path=coordinates,
layer_widths=layer_widths) layer_widths=layer_widths)
def add_wire(self, layers, coordinates): def add_wire(self, layers, coordinates):
"""Connects a routing path on given layer,coordinates,width. """Connects a routing path on given layer,coordinates,width.
The layers are the (horizontal, via, vertical). """ The layers are the (horizontal, via, vertical). """
import wire 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, wire.wire(obj=self,
layer_stack=layers, layer_stack=layers,
position_list=coordinates) position_list=coordinates)
@ -394,7 +459,7 @@ class layout():
def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None): def add_via(self, layers, offset, size=[1,1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """ """ Add a three layer via structure. """
if directions==None: if not directions:
directions = (self.get_preferred_direction(layers[0]), directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2])) self.get_preferred_direction(layers[2]))
@ -414,9 +479,12 @@ class layout():
return inst return inst
def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): 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]), directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2])) self.get_preferred_direction(layers[2]))
@ -430,7 +498,8 @@ class layout():
height = via.height height = via.height
width = via.width 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) self.add_mod(via)
inst = self.add_inst(name=via.name, inst = self.add_inst(name=via.name,
@ -454,8 +523,6 @@ class layout():
rotate=rotate) rotate=rotate)
return inst return inst
def gds_read(self): def gds_read(self):
"""Reads a GDSII file in the library and checks if it exists """Reads a GDSII file in the library and checks if it exists
Otherwise, start a new layout for dynamic generation.""" Otherwise, start a new layout for dynamic generation."""
@ -480,7 +547,7 @@ class layout():
def print_gds(self, gds_file=None): def print_gds(self, gds_file=None):
"""Print the gds file (not the vlsi class) to the terminal """ """Print the gds file (not the vlsi class) to the terminal """
if gds_file == None: if not gds_file:
gds_file = self.gds_file gds_file = self.gds_file
debug.info(4, "Printing {}".format(gds_file)) debug.info(4, "Printing {}".format(gds_file))
arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"]) arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"])
@ -507,11 +574,12 @@ class layout():
# If it's not a premade cell # If it's not a premade cell
# and we didn't add our own boundary, # and we didn't add our own boundary,
# we should add a boundary just for DRC in some technologies # we should add a boundary just for DRC in some technologies
if not self.is_library_cell and not self.boundary: if not self.is_library_cell and not self.bounding_box:
# If there is a boundary layer, and we didn't create one, add one. # If there is a boundary layer, and we didn't create one, add one.
if "stdc" in techlayer.keys(): if "stdc" in techlayer.keys():
boundary_layer = "stdc" 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] height = boundary[1][1] - boundary[0][1]
width = boundary[1][0] - boundary[0][0] width = boundary[1][0] - boundary[0][0]
(layer_number, layer_purpose) = techlayer[boundary_layer] (layer_number, layer_purpose) = techlayer[boundary_layer]
@ -523,7 +591,6 @@ class layout():
center=False) center=False)
debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary)) debug.info(2, "Adding {0} boundary {1}".format(self.name, boundary))
self.visited.append(self.name) self.visited.append(self.name)
def gds_write(self, gds_name): def gds_write(self, gds_name):
@ -559,7 +626,8 @@ class layout():
def get_blockages(self, layer, top_level=False): 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. Do not write the pins since they aren't obstructions.
""" """
if type(layer) == str: if type(layer) == str:
@ -634,7 +702,6 @@ class layout():
vertical=False, vertical=False,
make_pins=False) make_pins=False)
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins): def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
""" """
Create a horizontal or vertical bus. It can be either just rectangles, or actual Create a horizontal or vertical bus. It can be either just rectangles, or actual
@ -660,10 +727,12 @@ class layout():
offset=line_offset, offset=line_offset,
height=length) height=length)
# Make this the center of the rail # 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: else:
for i in range(len(names)): 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: if make_pins:
self.add_layout_pin(text=names[i], self.add_layout_pin(text=names[i],
layer=layer, layer=layer,
@ -674,7 +743,8 @@ class layout():
offset=line_offset, offset=line_offset,
width=length) width=length)
# Make this the center of the rail # 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 return line_positions
@ -692,7 +762,8 @@ class layout():
""" """
Connect a mapping of pin -> name for a bus. This could be Connect a mapping of pin -> name for a bus. This could be
replaced with a channel router in the future. 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 (horizontal_layer, via_layer, vertical_layer) = layer_stack
if horizontal: if horizontal:
@ -712,16 +783,16 @@ class layout():
# left/right then up/down # left/right then up/down
mid_pos = vector(bus_pos.x, pin_pos.y) 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 # Connect to the pin on the instances with a via if it is
# not on the right layer # not on the right layer
if pin.layer != route_layer: if pin.layer != route_layer:
self.add_via_center(layers=layer_stack, self.add_via_center(layers=layer_stack,
offset=pin_pos) 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 # We only need a via if they happened to align perfectly
# so the add_wire didn't add a via # so the add_wire didn't add a via
@ -729,20 +800,31 @@ class layout():
self.add_via_center(layers=layer_stack, self.add_via_center(layers=layer_stack,
offset=bus_pos, offset=bus_pos,
rotate=90) rotate=90)
def get_layer_pitch(self, layer): def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """ """ Return the track pitch on a given layer """
if layer == "m1": 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": 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": 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": elif layer == "m4":
from tech import layer as tech_layer from tech import layer as tech_layer
if "m4" in 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: 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: else:
debug.error("Cannot find layer pitch.") debug.error("Cannot find layer pitch.")
@ -752,18 +834,20 @@ class layout():
layer_stack, layer_stack,
pitch): 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]) max_x = max([pin.center().x for pin in pins])
min_x = min([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 we are less than a pitch, just create a non-preferred layer jog
if max_x-min_x <= pitch: if max_x-min_x <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)] half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
# Add the horizontal trunk on the 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 # Route each pin to the trunk
for pin in pins: for pin in pins:
@ -772,8 +856,9 @@ class layout():
self.add_path(self.vertical_layer, [pin.center(), mid]) self.add_path(self.vertical_layer, [pin.center(), mid])
else: else:
# Add the horizontal trunk # Add the horizontal trunk
self.add_path(self.horizontal_layer,[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)]) self.add_path(self.horizontal_layer,
trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y) [vector(min_x, trunk_offset.y),
vector(max_x, trunk_offset.y)])
# Route each pin to the trunk # Route each pin to the trunk
for pin in pins: for pin in pins:
@ -788,7 +873,8 @@ class layout():
layer_stack, layer_stack,
pitch): 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]) max_y = max([pin.center().y for pin in pins])
min_y = min([pin.center().y for pin in pins]) min_y = min([pin.center().y for pin in pins])
@ -799,7 +885,9 @@ class layout():
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
# Add the vertical trunk on the 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 # Route each pin to the trunk
for pin in pins: for pin in pins:
@ -808,8 +896,9 @@ class layout():
self.add_path(self.horizontal_layer, [pin.center(), mid]) self.add_path(self.horizontal_layer, [pin.center(), mid])
else: else:
# Add the vertical trunk # Add the vertical trunk
self.add_path(self.vertical_layer,[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)]) self.add_path(self.vertical_layer,
trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),) [vector(trunk_offset.x, min_y),
vector(trunk_offset.x, max_y)])
# Route each pin to the trunk # Route each pin to the trunk
for pin in pins: for pin in pins:
@ -818,7 +907,6 @@ class layout():
self.add_via_center(layers=layer_stack, self.add_via_center(layers=layer_stack,
offset=mid) offset=mid)
def create_channel_route(self, netlist, def create_channel_route(self, netlist,
offset, offset,
layer_stack, layer_stack,
@ -848,7 +936,7 @@ class layout():
def vcg_nets_overlap(net1, net2, vertical, pitch): def vcg_nets_overlap(net1, net2, vertical, pitch):
""" """
Check all the pin pairs on two nets and return a pin 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: for pin1 in net1:
@ -861,7 +949,8 @@ class layout():
def vcg_pin_overlap(pin1, pin2, vertical, pitch): def vcg_pin_overlap(pin1, pin2, vertical, pitch):
""" Check for vertical or horizontal overlap of the two pins """ """ Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break. # 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. # extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set # Pin 1 must be in the "BOTTOM" set
@ -879,15 +968,18 @@ class layout():
self.vertical_layer = layer_stack[2] self.vertical_layer = layer_stack[2]
self.horizontal_layer = layer_stack[0] self.horizontal_layer = layer_stack[0]
(self.vertical_pitch,self.vertical_width,self.vertical_space) = self.get_layer_pitch(self.vertical_layer) layer_stuff = self.get_layer_pitch(self.vertical_layer)
(self.horizontal_pitch,self.horizontal_width,self.horizontal_space) = self.get_layer_pitch(self.horizontal_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
# FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the # too if we want to minimize the
# number of tracks! # number of tracks!
# hcg = {} # 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() vcg = collections.OrderedDict()
# Create names for the nets for the graphs # Create names for the nets for the graphs
@ -910,9 +1002,15 @@ class layout():
# Skip yourself # Skip yourself
if net_name1 == net_name2: if net_name1 == net_name2:
continue 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) 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) vcg[net_name2].append(net_name1)
# list of routes to do # list of routes to do
@ -929,8 +1027,6 @@ class layout():
# FIXME: We don't support cyclic VCGs right now. # FIXME: We don't support cyclic VCGs right now.
debug.error("Cyclic VCG in channel router.", -1) debug.error("Cyclic VCG in channel router.", -1)
# These are the pins we'll have to connect # These are the pins we'll have to connect
pin_list = nets[net_name] pin_list = nets[net_name]
# print("Routing:", net_name, [x.name for x in pin_list]) # print("Routing:", net_name, [x.name for x in pin_list])
@ -938,15 +1034,21 @@ class layout():
# Remove the net from other constriants in the VCG # Remove the net from other constriants in the VCG
vcg = remove_net_from_graph(net_name, 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: 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) offset += vector(self.vertical_pitch, 0)
else: 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) offset += vector(0, self.horizontal_pitch)
def create_vertical_channel_route(self, netlist, offset, layer_stack): def create_vertical_channel_route(self, netlist, offset, layer_stack):
""" """
Wrapper to create a vertical channel route Wrapper to create a vertical channel route
@ -961,17 +1063,20 @@ class layout():
def add_boundary(self, ll=vector(0, 0), ur=None): def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """ """ Add boundary for debugging dimensions """
if OPTS.netlist_only:
return
if "stdc" in techlayer.keys(): if "stdc" in techlayer.keys():
boundary_layer = "stdc" boundary_layer = "stdc"
else: else:
boundary_layer = "boundary" boundary_layer = "boundary"
if ur == None: if not ur:
self.boundary = self.add_rect(layer=boundary_layer, self.bounding_box = self.add_rect(layer=boundary_layer,
offset=ll, offset=ll,
height=self.height, height=self.height,
width=self.width) width=self.width)
else: else:
self.boundary = self.add_rect(layer=boundary_layer, self.bounding_box = self.add_rect(layer=boundary_layer,
offset=ll, offset=ll,
height=ur.y-ll.y, height=ur.y-ll.y,
width=ur.x-ll.x) width=ur.x-ll.x)
@ -996,15 +1101,19 @@ class layout():
width=xmax-xmin, width=xmax-xmin,
height=ymax-ymin) height=ymax-ymin)
def copy_power_pins(self, inst, name): 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) pins = inst.get_pins(name)
for pin in pins: for pin in pins:
if pin.layer == "m3": 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": elif pin.layer == "m1":
self.add_power_pin(name, pin.center()) self.add_power_pin(name, pin.center())
else: else:
@ -1028,13 +1137,11 @@ class layout():
offset=loc, offset=loc,
directions=direction) directions=direction)
if start_layer == "m1" or start_layer == "m2": if start_layer == "m1" or start_layer == "m2":
via = self.add_via_center(layers=self.m2_stack, via = self.add_via_center(layers=self.m2_stack,
size=size, size=size,
offset=loc, offset=loc,
directions=direction) directions=direction)
if start_layer == "m3": if start_layer == "m3":
self.add_layout_pin_rect_center(text=name, self.add_layout_pin_rect_center(text=name,
layer="m3", layer="m3",
@ -1048,10 +1155,11 @@ class layout():
def add_power_ring(self, bbox): def add_power_ring(self, bbox):
""" """
Create vdd and gnd power rings around an area of the bounding box argument. Must Create vdd and gnd power rings around an area of the bounding box
have a supply_rail_width and supply_rail_pitch defined as a member variable. argument. Must have a supply_rail_width and supply_rail_pitch
Defines local variables of the left/right/top/bottom vdd/gnd center offsets defined as a member variable. Defines local variables of the
for use in other modules.. left/right/top/bottom vdd/gnd center offsets for use in other
modules..
""" """
[ll, ur] = bbox [ll, ur] = bbox
@ -1061,15 +1169,16 @@ class layout():
width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails # 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", left_gnd_pin = self.add_layout_pin(text="gnd",
layer="m2", layer="m2",
offset=offset, offset=offset,
width=self.supply_rail_width, width=self.supply_rail_width,
height=height) height=height)
offset = ll + vector(-1 * self.supply_rail_pitch,
offset = ll + vector(-1*self.supply_rail_pitch, -1*self.supply_rail_pitch) -1 * self.supply_rail_pitch)
left_vdd_pin = self.add_layout_pin(text="vdd", left_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2", layer="m2",
offset=offset, offset=offset,
@ -1084,7 +1193,8 @@ class layout():
width=self.supply_rail_width, width=self.supply_rail_width,
height=height) 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", right_vdd_pin = self.add_layout_pin(text="vdd",
layer="m2", layer="m2",
offset=offset, offset=offset,
@ -1092,14 +1202,16 @@ class layout():
height=height) height=height)
# BOTTOM horizontal rails # 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", bottom_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1", layer="m1",
offset=offset, offset=offset,
width=width, width=width,
height=self.supply_rail_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", bottom_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1", layer="m1",
offset=offset, offset=offset,
@ -1107,14 +1219,16 @@ class layout():
height=self.supply_rail_width) height=self.supply_rail_width)
# TOP horizontal rails # 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", top_gnd_pin = self.add_layout_pin(text="gnd",
layer="m1", layer="m1",
offset=offset, offset=offset,
width=width, width=width,
height=self.supply_rail_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", top_vdd_pin = self.add_layout_pin(text="vdd",
layer="m1", layer="m1",
offset=offset, offset=offset,
@ -1132,7 +1246,6 @@ class layout():
self.top_gnd_y_center = top_gnd_pin.cy() self.top_gnd_y_center = top_gnd_pin.cy()
self.top_vdd_y_center = top_vdd_pin.cy() self.top_vdd_y_center = top_vdd_pin.cy()
# Find the number of vias for this pitch # Find the number of vias for this pitch
self.supply_vias = 1 self.supply_vias = 1
from sram_factory import factory from sram_factory import factory
@ -1158,12 +1271,14 @@ class layout():
for pt in via_points: for pt in via_points:
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pt, offset=pt,
size = (self.supply_vias, self.supply_vias)) size=(self.supply_vias,
self.supply_vias))
def pdf_write(self, pdf_name): 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" # self.pdf_name = self.name + ".pdf"
debug.info(0, "Writing to {}".format(pdf_name)) debug.info(0, "Writing to {}".format(pdf_name))
pdf = gdsMill.pdfLayout(self.gds) pdf = gdsMill.pdfLayout(self.gds)

View File

@ -129,6 +129,13 @@ class pin_layout:
self.rect = [vector(min_x, min_y), vector(max_x, max_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): def inflate(self, spacing=None):
""" """
Inflate the rectangle by the spacing (or other rule) Inflate the rectangle by the spacing (or other rule)

View File

@ -977,33 +977,36 @@ class pbitcell(bitcell_base.bitcell_base):
""" """
# extend pwell to encompass entire nmos region of the cell up to the # extend pwell to encompass entire nmos region of the cell up to the
# height of the tallest nmos transistor # height of the tallest nmos transistor
max_nmos_well_height = max(self.inverter_nmos.cell_well_height, max_nmos_well_height = max(self.inverter_nmos.well_height,
self.readwrite_nmos.cell_well_height, self.readwrite_nmos.well_height,
self.write_nmos.cell_well_height, self.write_nmos.well_height,
self.read_nmos.cell_well_height) self.read_nmos.well_height)
well_height = max_nmos_well_height + self.port_ypos \ well_height = max_nmos_well_height + self.port_ypos \
- self.well_enclose_active - self.gnd_position.y - self.nwell_enclose_active - self.gnd_position.y
offset = vector(self.leftmost_xpos, self.botmost_ypos) # 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", self.add_rect(layer="pwell",
offset=offset, offset=offset,
width=self.width, width=well_width,
height=well_height) height=well_height)
# extend nwell to encompass inverter_pmos # extend nwell to encompass inverter_pmos
# calculate offset of the left pmos well # calculate offset of the left pmos well
inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ 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 \ 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 width of the two combined nwells
# calculate height to encompass nimplant connected to vdd # calculate height to encompass nimplant connected to vdd
well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ 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 \ 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", self.add_rect(layer="nwell",
offset=offset, offset=offset,
width=well_width, width=well_width,

View File

@ -333,7 +333,7 @@ class bank(design.design):
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
# A space for wells or jogging m2 # A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch) 3*self.m2_pitch)

View File

@ -149,7 +149,7 @@ class port_address(design.design):
""" """
# A space for wells or jogging m2 # A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch) 3*self.m2_pitch)
row_decoder_offset = vector(0,0) row_decoder_offset = vector(0,0)

View File

@ -212,7 +212,7 @@ class port_data(design.design):
# A space for wells or jogging m2 between modules # A space for wells or jogging m2 between modules
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclose_active"), self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch) 3*self.m2_pitch)

View File

@ -8,7 +8,7 @@
import contact import contact
import design import design
import debug import debug
from tech import layer, drc from tech import layer
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
@ -126,29 +126,35 @@ class pgate(design.design):
def extend_wells(self, middle_position): def extend_wells(self, middle_position):
""" Extend the n/p wells to cover whole cell """ """ Extend the n/p wells to cover whole cell """
# FIXME: float rounding problem
middle_position = middle_position.snap_to_grid()
# Add a rail width to extend the well to the top of the rail # Add a rail width to extend the well to the top of the rail
max_y_offset = self.height + 0.5 * self.m1_width nwell_max_offset = max(self.find_highest_layer_coords("nwell").y,
self.nwell_position = middle_position self.height + 0.5 * self.m1_width)
nwell_height = max_y_offset - middle_position.y nwell_position = middle_position
if layer["nwell"]: nwell_height = nwell_max_offset - middle_position.y
if "nwell" in layer:
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=middle_position, offset=middle_position,
width=self.well_width, width=self.well_width,
height=nwell_height) height=nwell_height)
if layer["vtg"]: if "vtg" in layer:
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=self.nwell_position, offset=nwell_position,
width=self.well_width, width=self.well_width,
height=nwell_height) height=nwell_height)
pwell_position = vector(0, -0.5 * self.m1_width) # Start this half a rail width below the cell
pwell_min_offset = min(self.find_lowest_layer_coords("pwell").y,
-0.5 * self.m1_width)
pwell_position = vector(0, pwell_min_offset)
pwell_height = middle_position.y - pwell_position.y pwell_height = middle_position.y - pwell_position.y
if layer["pwell"]: if "pwell" in layer:
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=pwell_position, offset=pwell_position,
width=self.well_width, width=self.well_width,
height=pwell_height) height=pwell_height)
if layer["vtg"]: if "vtg" in layer:
self.add_rect(layer="vtg", self.add_rect(layer="vtg",
offset=pwell_position, offset=pwell_position,
width=self.well_width, width=self.well_width,
@ -168,7 +174,7 @@ class pgate(design.design):
# OR align the active with the top of PMOS active. # OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5 * self.m1_width 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, 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) contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y # Offset by half a contact in x and y
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width, contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
@ -220,7 +226,7 @@ class pgate(design.design):
# Must be at least an well enclosure of active up # Must be at least an well enclosure of active up
# from the bottom of the well # from the bottom of the well
contact_yoffset = max(nmos_pos.y, contact_yoffset = max(nmos_pos.y,
self.well_enclose_active \ self.nwell_enclose_active \
- nmos.active_contact.first_layer_height / 2) - nmos.active_contact.first_layer_height / 2)
contact_offset = vector(contact_xoffset, contact_yoffset) contact_offset = vector(contact_xoffset, contact_yoffset)

View File

@ -153,7 +153,7 @@ class pinv(pgate.pgate):
# the well width is determined the multi-finger PMOS device width plus # the well width is determined the multi-finger PMOS device width plus
# the well contact width and half well enclosure on both sides # the well contact width and half well enclosure on both sides
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ self.active_space + 2*self.well_enclose_active + self.active_space + 2*self.nwell_enclose_active
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
@ -223,7 +223,7 @@ class pinv(pgate.pgate):
self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y)) self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y))
# This will help with the wells # This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy()) self.well_pos = self.output_pos
def route_outputs(self): def route_outputs(self):
""" """

View File

@ -100,7 +100,7 @@ class pnand2(pgate.pgate):
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + contact.activem1.width \ self.well_width = 2 * self.pmos.active_width + contact.activem1.width \
+ 2 * self.active_space \ + 2 * self.active_space \
+ 2 * self.well_enclose_active + 2 * self.nwell_enclose_active
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
@ -171,7 +171,7 @@ class pnand2(pgate.pgate):
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells # This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy()) self.well_pos = self.output_pos
def add_well_contacts(self): def add_well_contacts(self):
""" """

View File

@ -92,14 +92,11 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2 * self.active_space + 2 * self.well_enclose_active \ + 2 * self.active_space + 2 * self.nwell_enclose_active \
- self.overlap_offset.x - self.overlap_offset.x
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # 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 # This is the extra space needed to ensure DRC rules
# to the active contacts # to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos") nmos = factory.create(module_type="ptx", tx_type="nmos")
@ -179,8 +176,11 @@ class pnand3(pgate.pgate):
self.nmos3_pos = nmos2_pos + self.overlap_offset self.nmos3_pos = nmos2_pos + self.overlap_offset
self.nmos3_inst.place(self.nmos3_pos) self.nmos3_inst.place(self.nmos3_pos)
# This will help with the wells and the input/output placement
self.output_pos = vector(0, 0.5*self.height)
# This should be placed at the top of the NMOS well # This should be placed at the top of the NMOS well
self.well_pos = vector(0, self.nmos1_inst.uy()) self.well_pos = self.output_pos
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """

View File

@ -98,7 +98,7 @@ class pnor2(pgate.pgate):
self.well_width = 2 * self.pmos.active_width \ self.well_width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \ + self.pmos.active_contact.width \
+ 2 * self.active_space \ + 2 * self.active_space \
+ 2 * self.well_enclose_active + 2 * self.nwell_enclose_active
self.width = self.well_width self.width = self.well_width
# Height is an input parameter, so it is not recomputed. # Height is an input parameter, so it is not recomputed.
@ -136,7 +136,6 @@ class pnor2(pgate.pgate):
mod=self.pmos) mod=self.pmos)
self.connect_inst(["net1", "B", "Z", "vdd"]) self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst = self.add_inst(name="pnor2_nmos1", self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"]) self.connect_inst(["Z", "A", "gnd", "gnd"])
@ -170,7 +169,7 @@ class pnor2(pgate.pgate):
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height)) 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells # This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy()) self.well_pos = self.output_pos
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """

View File

@ -116,7 +116,7 @@ class precharge(design.design):
# adds the lower pmos to layout # adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.well_enclose_active), self.nwell_enclose_active),
self.pmos.active_offset.y) self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position) self.lower_pmos_inst.place(self.lower_pmos_position)
@ -176,7 +176,7 @@ class precharge(design.design):
# adds the contact from active to metal1 # adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.activem1.height / 2 \ + vector(0, self.upper_pmos1_inst.uy() + contact.activem1.height / 2 \
+ self.well_extend_active) + self.nwell_extend_active)
self.add_via_center(layers=self.active_stack, self.add_via_center(layers=self.active_stack,
offset=well_contact_pos, offset=well_contact_pos,
implant_type="n", implant_type="n",

View File

@ -61,7 +61,6 @@ class ptristate_inv(pgate.pgate):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"]) self.add_pin_list(["in", "out", "en", "en_bar", "vdd", "gnd"])
def setup_layout_constants(self): def setup_layout_constants(self):
""" """
Pre-compute some handy layout parameters. Pre-compute some handy layout parameters.
@ -73,7 +72,7 @@ class ptristate_inv(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2 * self.pmos.active_width + self.well_enclose_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 # 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 self.width = self.well_width + 0.5 * self.m1_space
@ -82,7 +81,6 @@ class ptristate_inv(pgate.pgate):
# Make sure we can put a well above and below # Make sure we can put a well above and below
self.top_bottom_space = max(contact.activem1.width, contact.activem1.height) self.top_bottom_space = max(contact.activem1.width, contact.activem1.height)
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx", self.nmos = factory.create(module_type="ptx",
@ -95,7 +93,6 @@ class ptristate_inv(pgate.pgate):
width=self.pmos_width, width=self.pmos_width,
mults=1, mults=1,
tx_type="pmos") tx_type="pmos")
self.add_mod(self.pmos) self.add_mod(self.pmos)
def route_supply_rails(self): def route_supply_rails(self):

View File

@ -58,9 +58,9 @@ class ptx(design.design):
# some transistor sizes in other netlist depend on pbitcell # some transistor sizes in other netlist depend on pbitcell
self.create_layout() self.create_layout()
#ll = self.find_lowest_coords() ll = self.find_lowest_coords()
#ur = self.find_highest_coords() ur = self.find_highest_coords()
#self.add_boundary(ll, ur) self.add_boundary(ll, ur)
# (0,0) will be the corner of the 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) self.translate_all(self.active_offset)
@ -108,7 +108,7 @@ class ptx(design.design):
Pre-compute some handy layout parameters. Pre-compute some handy layout parameters.
""" """
if self.num_contacts==None: if not self.num_contacts:
self.num_contacts = self.calculate_num_contacts() self.num_contacts = self.calculate_num_contacts()
# Determine layer types needed # Determine layer types needed
@ -121,32 +121,33 @@ class ptx(design.design):
else: else:
self.error("Invalid transitor type.", -1) self.error("Invalid transitor type.", -1)
# This is not actually instantiated but used for calculations # This is not actually instantiated but used for calculations
self.active_contact = factory.create(module_type="contact", self.active_contact = factory.create(module_type="contact",
layer_stack=self.active_stack, layer_stack=self.active_stack,
directions=("V", "V"), directions=("V", "V"),
dimensions=(1, self.num_contacts)) dimensions=(1, self.num_contacts))
# The contacted poly pitch
# 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_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
self.poly_space) self.poly_space)
# The contacted poly pitch (or uncontacted in an odd technology) # The contacted poly pitch
self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width self.contact_spacing = 2 * self.contact_to_gate + \
self.contact_width + self.poly_width
# The enclosure of an active contact. Not sure about second term. # This is measured because of asymmetric enclosure rules
active_enclose_contact = max(self.active_enclose_contact, active_enclose_contact = 0.5*(self.active_contact.width - self.contact_width)
(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 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, # Active width is determined by enclosure on both ends and contacted pitch,
# at least one poly and n-1 poly pitches # 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 # Active height is just the transistor width
self.active_height = self.tx_width self.active_height = self.tx_width
@ -154,32 +155,35 @@ class ptx(design.design):
# Poly height must include poly extension over active # Poly height must include poly extension over active
self.poly_height = self.tx_width + 2 * self.poly_extend_active self.poly_height = self.tx_width + 2 * self.poly_extend_active
well_name = "{}well".format(self.well_type)
# The active offset is due to the well extension # The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active] * 2) if well_name in layer:
well_enclose_active = drc(well_name + "_enclose_active")
self.active_offset = vector([well_enclose_active] * 2)
else:
self.active_offset = vector(0, 0)
# Well enclosure of active, ensure minwidth as well # Well enclosure of active, ensure minwidth as well
well_name = "{}well".format(self.well_type) if well_name in layer:
if layer[well_name]: well_width_rule = drc("minwidth_" + well_name)
self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active, well_enclose_active = drc(well_name + "_enclose_active")
self.well_width) self.well_width = max(self.active_width + 2 * well_enclose_active,
self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active, well_width_rule)
self.well_width) 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 # 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.height = self.well_height - self.active_offset.y
self.width = self.cell_well_width - self.active_offset.x self.width = self.well_width - self.active_offset.x
else: 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.height = self.poly_height
self.width = self.active_width 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) # 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) 0.5 * self.active_height)
# Min area results are just flagged for now. # Min area results are just flagged for now.
debug.check(self.active_width * self.active_height >= self.minarea_active, debug.check(self.active_width * self.active_height >= self.minarea_active,
"Minimum active area violated.") "Minimum active area violated.")
@ -215,14 +219,14 @@ class ptx(design.design):
poly_offset = poly_positions[0] + vector(-0.5 * self.poly_width, poly_offset = poly_positions[0] + vector(-0.5 * self.poly_width,
distance_above_active) distance_above_active)
# Remove the old pin and add the new one # 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", self.add_layout_pin(text="G",
layer="poly", layer="poly",
offset=poly_offset, offset=poly_offset,
width=poly_width, width=poly_width,
height=self.poly_width) height=self.poly_width)
def connect_fingered_active(self, drain_positions, source_positions): def connect_fingered_active(self, drain_positions, source_positions):
""" """
Connect each contact up/down to a source or drain pin Connect each contact up/down to a source or drain pin
@ -230,10 +234,10 @@ class ptx(design.design):
# This is the distance that we must route up or down from the center # 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 # 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,
+ self.m1_space + 0.5 * self.m1_width) 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 # 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, # drains always go to the MIDDLE of the cell,
# so top of NMOS, bottom of PMOS # so top of NMOS, bottom of PMOS
@ -247,7 +251,8 @@ class ptx(design.design):
if len(source_positions) > 1: if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir, source_dir) 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 # Add each vertical segment
for a in source_positions: for a in source_positions:
self.add_path(("m1"), self.add_path(("m1"),
@ -326,17 +331,26 @@ class ptx(design.design):
Add an (optional) well and implant for the type of transistor. Add an (optional) well and implant for the type of transistor.
""" """
well_name = "{}well".format(self.well_type) well_name = "{}well".format(self.well_type)
if layer[well_name]: if not (well_name in layer or "vtg" in layer):
self.add_rect(layer=well_name, return
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)
center_pos = self.active_offset + vector(self.width / 2.0,
self.height / 2.0)
well_ll = center_pos - vector(self.well_width / 2.0,
self.well_height / 2.0)
well_ll = well_ll - vector(0,
self.poly_extend_active)
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): def calculate_num_contacts(self):
""" """
@ -345,7 +359,6 @@ class ptx(design.design):
""" """
return 1 return 1
def get_contact_positions(self): def get_contact_positions(self):
""" """
Create a list of the centers of drain and source contact positions. Create a list of the centers of drain and source contact positions.
@ -359,10 +372,10 @@ class ptx(design.design):
for i in range(self.mults): for i in range(self.mults):
if i%2: if i%2:
# It's a source... so offset from previous drain. # 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: else:
# It's a drain... so offset from previous source. # 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] return [source_positions,drain_positions]
@ -377,7 +390,7 @@ class ptx(design.design):
contact=self.add_via_center(layers=self.active_stack, contact=self.add_via_center(layers=self.active_stack,
offset=pos, offset=pos,
size=(1, self.num_contacts), size=(1, self.num_contacts),
directions=("H","V"), directions=("V","V"),
implant_type=self.implant_type, implant_type=self.implant_type,
well_type=self.well_type) well_type=self.well_type)
self.add_layout_pin_rect_center(text="S", self.add_layout_pin_rect_center(text="S",
@ -391,7 +404,7 @@ class ptx(design.design):
contact=self.add_via_center(layers=self.active_stack, contact=self.add_via_center(layers=self.active_stack,
offset=pos, offset=pos,
size=(1, self.num_contacts), size=(1, self.num_contacts),
directions=("H","V"), directions=("V","V"),
implant_type=self.implant_type, implant_type=self.implant_type,
well_type=self.well_type) well_type=self.well_type)
self.add_layout_pin_rect_center(text="D", self.add_layout_pin_rect_center(text="D",

View File

@ -44,8 +44,6 @@ class pwrite_driver(design.design):
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
@ -56,8 +54,6 @@ class pwrite_driver(design.design):
self.route_wires() self.route_wires()
self.route_supplies() self.route_supplies()
def add_pins(self): def add_pins(self):
self.add_pin("din", "INPUT") self.add_pin("din", "INPUT")
self.add_pin("bl", "OUTPUT") self.add_pin("bl", "OUTPUT")
@ -66,17 +62,19 @@ class pwrite_driver(design.design):
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
# Tristate inverter # Tristate inverter
self.tri = factory.create(module_type="ptristate_inv", height="min") self.tri = factory.create(module_type="ptristate_inv", height="min")
self.add_mod(self.tri) 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) #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 # Inverter for din and en
self.inv = factory.create(module_type="pinv", under_rail_vias=True) self.inv = factory.create(module_type="pinv", under_rail_vias=True)

View File

@ -12,6 +12,11 @@ from module_type import *
""" """
File containing the process technology parameters for FreePDK 45nm. File containing the process technology parameters for FreePDK 45nm.
""" """
###################################################
# Custom modules
###################################################
# This uses the default classes to instantiate module from # This uses the default classes to instantiate module from
# '$OPENRAM_HOME/compiler/modules'. # '$OPENRAM_HOME/compiler/modules'.
# Using tech_modules['cellname'] you can override each class by providing a custom # Using tech_modules['cellname'] you can override each class by providing a custom
@ -19,7 +24,11 @@ File containing the process technology parameters for FreePDK 45nm.
# For example: tech_modules['contact'] = 'contact_freepdk45' # For example: tech_modules['contact'] = 'contact_freepdk45'
tech_modules = ModuleType() tech_modules = ModuleType()
###################################################
# GDS file info # GDS file info
###################################################
GDS = {} GDS = {}
# gds units # gds units
# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first # From http://www.cnf.cornell.edu/cnf_spie9.html: "The first
@ -136,7 +145,10 @@ drc["minlength_channel"] = 0.05
drc["pwell_to_nwell"] = 0.225 drc["pwell_to_nwell"] = 0.225
# WELL.3 Minimum spacing of nwell/pwell at the same potential # WELL.3 Minimum spacing of nwell/pwell at the same potential
# WELL.4 Minimum width of nwell/pwell # WELL.4 Minimum width of nwell/pwell
drc.add_layer("well", drc.add_layer("nwell",
width = 0.2,
spacing = 0.135)
drc.add_layer("pwell",
width = 0.2, width = 0.2,
spacing = 0.135) spacing = 0.135)
@ -165,7 +177,10 @@ drc.add_layer("active",
width = 0.09, width = 0.09,
spacing = 0.08) spacing = 0.08)
# ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active # ACTIVE.3 Minimum enclosure/spacing of nwell/pwell to active
drc.add_enclosure("well", drc.add_enclosure("nwell",
layer = "active",
enclosure = 0.055)
drc.add_enclosure("pwell",
layer = "active", layer = "active",
enclosure = 0.055) enclosure = 0.055)

View File

@ -12,6 +12,11 @@ from module_type import *
""" """
File containing the process technology parameters for SCMOS 4m, 0.35um File containing the process technology parameters for SCMOS 4m, 0.35um
""" """
###################################################
# Custom modules
###################################################
# This uses the default classes to instantiate module from # This uses the default classes to instantiate module from
# '$OPENRAM_HOME/compiler/modules'. # '$OPENRAM_HOME/compiler/modules'.
# Using tech_modules['cellname'] you can override each class by providing a custom # Using tech_modules['cellname'] you can override each class by providing a custom
@ -19,7 +24,10 @@ File containing the process technology parameters for SCMOS 4m, 0.35um
# For example: tech_modules['contact'] = 'contact_scn4m' # For example: tech_modules['contact'] = 'contact_scn4m'
tech_modules = ModuleType() tech_modules = ModuleType()
###################################################
# GDS file info # GDS file info
###################################################
GDS={} GDS={}
# gds units # gds units
# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first # From http://www.cnf.cornell.edu/cnf_spie9.html: "The first
@ -69,8 +77,6 @@ preferred_directions = {"poly": "V",
# create the GDS layer map # create the GDS layer map
layer={} layer={}
layer["vtg"] = None
layer["vth"] = None
layer["pwell"] = (41, 0) layer["pwell"] = (41, 0)
layer["nwell"] = (42, 0) layer["nwell"] = (42, 0)
layer["active"] = (43, 0) layer["active"] = (43, 0)
@ -124,7 +130,10 @@ drc["minlength_channel"] = 2*_lambda_
drc["pwell_to_nwell"] = 0 drc["pwell_to_nwell"] = 0
# 1.3 Minimum spacing between wells of same type (if both are drawn) # 1.3 Minimum spacing between wells of same type (if both are drawn)
# 1.1 Minimum width # 1.1 Minimum width
drc.add_layer("well", drc.add_layer("nwell",
width = 12*_lambda_,
spacing = 6*_lambda_)
drc.add_layer("pwell",
width = 12*_lambda_, width = 12*_lambda_,
spacing = 6*_lambda_) spacing = 6*_lambda_)
@ -151,7 +160,10 @@ drc.add_layer("active",
spacing = 4*_lambda_) spacing = 4*_lambda_)
# 2.3 Source/drain active to well edge # 2.3 Source/drain active to well edge
drc.add_enclosure("well", drc.add_enclosure("nwell",
layer = "active",
enclosure = 6*_lambda_)
drc.add_enclosure("pwell",
layer = "active", layer = "active",
enclosure = 6*_lambda_) enclosure = 6*_lambda_)