Add layer-purpose GDS support. Various PEP8 fixes.

This commit is contained in:
Matthew Guthaus 2019-11-14 18:17:20 +00:00
parent bba6995653
commit 131f4bda4a
11 changed files with 267 additions and 198 deletions

View File

@ -28,8 +28,8 @@ class pin_layout:
# snap the rect to the grid
self.rect = [x.snap_to_grid() for x in self.rect]
debug.check(self.width() > 0,"Zero width pin.")
debug.check(self.height() > 0,"Zero height pin.")
debug.check(self.width() > 0, "Zero width pin.")
debug.check(self.height() > 0, "Zero height pin.")
# if it's a string, use the name
if type(layer_name_pp) == str:
@ -37,7 +37,7 @@ class pin_layout:
# else it is required to be a lpp
else:
for (layer_name, lpp) in layer.items():
if layer_name_pp[0] == lpp[0] and (not layer_name_pp[1] or layer_name_pp[1]==lpp[1]):
if self.same_lpp(layer_name_pp, lpp):
self.layer = layer_name
break
else:
@ -78,7 +78,7 @@ class pin_layout:
def __eq__(self, other):
""" Check if these are the same pins for duplicate checks """
if isinstance(other, self.__class__):
return (self.layer == other.layer and self.rect == other.rect)
return (self.lpp == other.lpp and self.rect == other.rect)
else:
return False
@ -177,7 +177,7 @@ class pin_layout:
return True
# Can only overlap on the same layer
if self.layer != other.layer:
if not self.same_lpp(self.lpp, other.lpp):
return False
if not self.xcontains(other):
@ -198,7 +198,7 @@ class pin_layout:
def overlaps(self, other):
""" Check if a shape overlaps with a rectangle """
# Can only overlap on the same layer
if self.layer != other.layer:
if not self.same_lpp(self.lpp, other.lpp):
return False
x_overlaps = self.xoverlaps(other)
@ -506,3 +506,13 @@ class pin_layout:
return r
return None
def same_lpp(self, lpp1, lpp2):
"""
Check if the layers and purposes are the same.
Ignore if purpose is a None.
"""
if lpp1[1] == None or lpp2[1] == None:
return lpp1[0] == lpp2[0]
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]

View File

@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import os
import gdsMill
import tech
import math
@ -16,6 +15,7 @@ from pin_layout import pin_layout
OPTS = globals.OPTS
def ceil(decimal):
"""
Performs a ceiling function on the decimal place specified by the DRC grid.
@ -23,27 +23,33 @@ def ceil(decimal):
grid = tech.drc["grid"]
return math.ceil(decimal * 1 / grid) / (1 / grid)
def round_to_grid(number):
"""
Rounds an arbitrary number to the grid.
"""
grid = tech.drc["grid"]
grid = tech.drc["grid"]
# this gets the nearest integer value
number_grid = int(round(round((number / grid), 2), 0))
number_off = number_grid * grid
return number_off
def snap_to_grid(offset):
"""
Changes the coodrinate to match the grid settings
"""
return [round_to_grid(offset[0]),round_to_grid(offset[1])]
return [round_to_grid(offset[0]),
round_to_grid(offset[1])]
def pin_center(boundary):
"""
This returns the center of a pin shape in the vlsiLayout border format.
"""
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
return [0.5 * (boundary[0] + boundary[2]),
0.5 * (boundary[1] + boundary[3])]
def auto_measure_libcell(pin_list, name, units, lpp):
"""
@ -57,35 +63,34 @@ def auto_measure_libcell(pin_list, name, units, lpp):
cell = {}
measure_result = cell_vlsi.getLayoutBorder(lpp[0])
if measure_result == None:
if measure_result:
measure_result = cell_vlsi.measureSize(name)
[cell["width"], cell["height"]] = measure_result
for pin in pin_list:
(name,lpp,boundary)=cell_vlsi.getPinShapeByLabel(str(pin))
(name, lpp, boundary) = cell_vlsi.getPinShapeByLabel(str(pin))
cell[str(pin)] = pin_center(boundary)
return cell
def get_gds_size(name, gds_filename, units, lpp):
"""
Open a GDS file and return the size from either the
bounding box or a border layer.
"""
debug.info(4,"Creating VLSI layout for {}".format(name))
debug.info(4, "Creating VLSI layout for {}".format(name))
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename)
cell = {}
measure_result = cell_vlsi.getLayoutBorder(lpp)
if measure_result == None:
debug.info(2,"Layout border failed. Trying to measure size for {}".format(name))
if not measure_result:
debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
measure_result = cell_vlsi.measureSize(name)
# returns width,height
return measure_result
def get_libcell_size(name, units, lpp):
"""
Open a GDS file and return the library cell size from either the
@ -106,15 +111,18 @@ def get_gds_pins(pin_names, name, gds_filename, units):
cell = {}
for pin_name in pin_names:
cell[str(pin_name)]=[]
pin_list=cell_vlsi.getPinShape(str(pin_name))
cell[str(pin_name)] = []
pin_list = cell_vlsi.getPinShape(str(pin_name))
for pin_shape in pin_list:
(lpp,boundary)=pin_shape
rect=[vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])]
# this is a list because other cells/designs may have must-connect pins
(lpp, boundary) = pin_shape
rect = [vector(boundary[0], boundary[1]),
vector(boundary[2], boundary[3])]
# this is a list because other cells/designs
# may have must-connect pins
cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
return cell
def get_libcell_pins(pin_list, name, units):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.

View File

@ -27,9 +27,9 @@ def check(check, str):
os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0:
import pdb; pdb.set_trace()
else:
assert 0
import pdb
pdb.set_trace()
assert 0
def error(str, return_value=0):
@ -41,9 +41,9 @@ def error(str, return_value=0):
os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0:
import pdb; pdb.set_trace()
else:
assert return_value == 0
import pdb
pdb.set_trace()
assert return_value == 0
def warning(str):

View File

@ -213,11 +213,11 @@ class VlsiLayout:
def initialize(self):
self.deduceHierarchy()
#self.traverseTheHierarchy()
# self.traverseTheHierarchy()
self.populateCoordinateMap()
for layerNumber in self.layerNumbersInUse:
self.processLabelPins((layerNumber,None))
self.processLabelPins((layerNumber, None))
def populateCoordinateMap(self):
@ -245,21 +245,24 @@ class VlsiLayout:
self.xyTree.append((startingStructureName,origin,uVector,vVector))
self.traverseTheHierarchy(delegateFunction = addToXyTree)
def microns(self,userUnits):
def microns(self, userUnits):
"""Utility function to convert user units to microns"""
userUnit = self.units[1]/self.units[0]
userUnitsPerMicron = userUnit / (userunit)
userUnitsPerMicron = userUnit / userunit
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
return userUnits / layoutUnitsPerMicron
def userUnits(self,microns):
def userUnits(self, microns):
"""Utility function to convert microns to user units"""
userUnit = self.units[1]/self.units[0]
#userUnitsPerMicron = userUnit / 1e-6
# userUnitsPerMicron = userUnit / 1e-6
userUnitsPerMicron = userUnit / (userUnit)
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
#print("userUnit:",userUnit,"userUnitsPerMicron",userUnitsPerMicron,"layoutUnitsPerMicron",layoutUnitsPerMicron,[microns,microns*layoutUnitsPerMicron])
return round(microns*layoutUnitsPerMicron,0)
# print("userUnit:",userUnit,
# "userUnitsPerMicron",userUnitsPerMicron,
# "layoutUnitsPerMicron",layoutUnitsPerMicron,
# [microns,microns*layoutUnitsPerMicron])
return round(microns*layoutUnitsPerMicron, 0)
def changeRoot(self,newRoot, create=False):
"""
@ -386,7 +389,7 @@ class VlsiLayout:
#add the sref to the root structure
self.structures[self.rootStructureName].boundaries.append(boundaryToAdd)
def addPath(self, layerNumber=0, purposeNumber = None, coordinates=[(0,0)], width=1.0):
def addPath(self, layerNumber=0, purposeNumber=None, coordinates=[(0,0)], width=1.0):
"""
Method to add a path to a layout
"""
@ -405,7 +408,7 @@ class VlsiLayout:
#add the sref to the root structure
self.structures[self.rootStructureName].paths.append(pathToAdd)
def addText(self, text, layerNumber=0, purposeNumber = None, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
def addText(self, text, layerNumber=0, purposeNumber=None, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
textToAdd = GdsText()
textToAdd.drawingLayer = layerNumber
@ -593,42 +596,50 @@ class VlsiLayout:
passFailIndex += 1
print("Done\n\n")
def getLayoutBorder(self,lpp):
cellSizeMicron=None
def getLayoutBorder(self, lpp):
cellSizeMicron = None
for boundary in self.structures[self.rootStructureName].boundaries:
if boundary.drawingLayer==lpp[0] and \
(lpp[1]==None or boundary.purposeLayer==None or boundary.purposeLayer==lpp[1]):
if sameLPP((boundary.drawingLayer, boundary.purposeLayer),
lpp):
if self.debug:
debug.info(1,"Find border "+str(boundary.coordinates))
left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2]
cellSize=[right_top[0]-left_bottom[0],right_top[1]-left_bottom[1]]
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
debug.info(1, "Find border "+str(boundary.coordinates))
left_bottom = boundary.coordinates[0]
right_top = boundary.coordinates[2]
cellSize = [right_top[0]-left_bottom[0],
right_top[1]-left_bottom[1]]
cellSizeMicron = [cellSize[0]*self.units[0],
cellSize[1]*self.units[0]]
debug.check(cellSizeMicron,
"Error: "+str(self.rootStructureName)+".cell_size information not found yet")
return cellSizeMicron
def measureSize(self,startStructure):
self.rootStructureName=self.padText(startStructure)
def measureSize(self, startStructure):
self.rootStructureName = self.padText(startStructure)
self.populateCoordinateMap()
cellBoundary = [None, None, None, None]
for TreeUnit in self.xyTree:
cellBoundary=self.measureSizeInStructure(TreeUnit,cellBoundary)
cellSize=[cellBoundary[2]-cellBoundary[0],cellBoundary[3]-cellBoundary[1]]
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
cellBoundary = self.measureSizeInStructure(TreeUnit, cellBoundary)
cellSize = [cellBoundary[2]-cellBoundary[0],
cellBoundary[3]-cellBoundary[1]]
cellSizeMicron = [cellSize[0]*self.units[0],
cellSize[1]*self.units[0]]
return cellSizeMicron
def measureBoundary(self,startStructure):
self.rootStructureName=self.padText(startStructure)
def measureBoundary(self, startStructure):
self.rootStructureName = self.padText(startStructure)
self.populateCoordinateMap()
cellBoundary = [None, None, None, None]
for TreeUnit in self.xyTree:
cellBoundary=self.measureSizeInStructure(TreeUnit,cellBoundary)
return [[self.units[0]*cellBoundary[0],self.units[0]*cellBoundary[1]],
[self.units[0]*cellBoundary[2],self.units[0]*cellBoundary[3]]]
cellBoundary = self.measureSizeInStructure(TreeUnit, cellBoundary)
return [[self.units[0]*cellBoundary[0],
self.units[0]*cellBoundary[1]],
[self.units[0]*cellBoundary[2],
self.units[0]*cellBoundary[3]]]
def measureSizeInStructure(self,structure,cellBoundary):
(structureName,structureOrigin,structureuVector,structurevVector)=structure
def measureSizeInStructure(self, structure, cellBoundary):
(structureName, structureOrigin,
structureuVector, structurevVector) = structure
for boundary in self.structures[str(structureName)].boundaries:
left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2]
@ -655,14 +666,14 @@ class VlsiLayout:
cellBoundary[3]=right_top_Y
return cellBoundary
def getTexts(self, lpp):
"""
Get all of the labels on a given layer only at the root level.
"""
text_list = []
for Text in self.structures[self.rootStructureName].texts:
if Text.drawingLayer==lpp[0] and (lpp[1]==None or Text.purposeLayer==lpp[1]):
if sameLPP((Text.drawingLayer, Text.purposeLayer),
lpp):
text_list.append(Text)
return text_list
@ -678,9 +689,9 @@ class VlsiLayout:
max_pin = None
max_area = 0
for pin in pin_list:
(layer,boundary) = pin
(layer, boundary) = pin
new_area = boundaryArea(boundary)
if max_pin == None or new_area>max_area:
if not max_pin or new_area > max_area:
max_pin = pin
max_area = new_area
max_pins.append(max_pin)
@ -697,11 +708,10 @@ class VlsiLayout:
pin_map = self.pins[pin_name]
for pin_list in pin_map:
for pin in pin_list:
(pin_layer, boundary) = pin
(pin_layer, boundary) = pin
shape_list.append(pin)
return shape_list
def processLabelPins(self, lpp):
"""
@ -710,19 +720,21 @@ class VlsiLayout:
"""
# Get the labels on a layer in the root level
labels = self.getTexts(lpp)
# Get all of the shapes on the layer at all levels
# and transform them to the current level
shapes = self.getAllShapes(lpp)
shapes = self.getAllShapes(lpp)
for label in labels:
label_coordinate = label.coordinates[0]
user_coordinate = [x*self.units[0] for x in label_coordinate]
pin_shapes = []
for boundary in shapes:
if self.labelInRectangle(user_coordinate,boundary):
if self.labelInRectangle(user_coordinate, boundary):
pin_shapes.append((lpp, boundary))
label_text = label.textString
# Remove the padding if it exists
if label_text[-1] == "\x00":
label_text = label_text[0:-1]
@ -732,11 +744,11 @@ class VlsiLayout:
except KeyError:
self.pins[label_text] = []
self.pins[label_text].append(pin_shapes)
def getBlockages(self, lpp):
"""
Return all blockages on a given layer in [coordinate 1, coordinate 2,...] format and
Return all blockages on a given layer in
[coordinate 1, coordinate 2,...] format and
user units.
"""
blockages = []
@ -744,69 +756,85 @@ class VlsiLayout:
shapes = self.getAllShapes(lpp)
for boundary in shapes:
vectors = []
for i in range(0,len(boundary),2):
vectors.append(vector(boundary[i],boundary[i+1]))
for i in range(0, len(boundary), 2):
vectors.append(vector(boundary[i], boundary[i+1]))
blockages.append(vectors)
return blockages
return blockages
def getAllShapes(self, lpp):
"""
Return all shapes on a given layer in [llx, lly, urx, ury] format and user units for rectangles
and [coordinate 1, coordinate 2,...] format and user units for polygons.
Return all shapes on a given layer in [llx, lly, urx, ury]
format and user units for rectangles
and [coordinate 1, coordinate 2,...] format and user
units for polygons.
"""
boundaries = set()
for TreeUnit in self.xyTree:
#print(TreeUnit[0])
boundaries.update(self.getShapesInStructure(lpp,TreeUnit))
# print(TreeUnit[0])
boundaries.update(self.getShapesInStructure(lpp, TreeUnit))
# Convert to user units
user_boundaries = []
for boundary in boundaries:
boundaries_list = []
for i in range(0,len(boundary)):
for i in range(0, len(boundary)):
boundaries_list.append(boundary[i]*self.units[0])
user_boundaries.append(boundaries_list)
return user_boundaries
def getShapesInStructure(self, lpp, structure):
"""
Go through all the shapes in a structure and return the list of shapes in
the form [llx, lly, urx, ury] for rectangles and [coordinate 1, coordinate 2,...] for polygons.
"""
(structureName,structureOrigin,structureuVector,structurevVector)=structure
#print(structureName,"u",structureuVector.transpose(),"v",structurevVector.transpose(),"o",structureOrigin.transpose())
Go through all the shapes in a structure and
return the list of shapes in
the form [llx, lly, urx, ury] for rectangles
and [coordinate 1, coordinate 2,...] for polygons.
"""
(structureName, structureOrigin,
structureuVector, structurevVector) = structure
# print(structureName,
# "u", structureuVector.transpose(),
# "v",structurevVector.transpose(),
# "o",structureOrigin.transpose())
boundaries = []
for boundary in self.structures[str(structureName)].boundaries:
if boundary.drawingLayer==lpp[0] and (lpp[1]==None or boundary.purposeLayer==lpp[1]):
if len(boundary.coordinates)!=5:
if sameLPP((boundary.drawingLayer, boundary.purposeLayer),
lpp):
if len(boundary.coordinates) != 5:
# if shape is a polygon (used in DFF)
boundaryPolygon = []
# Polygon is a list of coordinates going ccw
for coord in range(0,len(boundary.coordinates)):
for coord in range(0, len(boundary.coordinates)):
boundaryPolygon.append(boundary.coordinates[coord][0])
boundaryPolygon.append(boundary.coordinates[coord][1])
# perform the rotation
boundaryPolygon=self.transformPolygon(boundaryPolygon,structureuVector,structurevVector)
# add the offset
boundaryPolygon = self.transformPolygon(boundaryPolygon,
structureuVector,
structurevVector)
# add the offset
polygon = []
for i in range(0,len(boundaryPolygon),2):
polygon.append(boundaryPolygon[i]+structureOrigin[0].item())
polygon.append(boundaryPolygon[i+1]+structureOrigin[1].item())
for i in range(0, len(boundaryPolygon), 2):
polygon.append(boundaryPolygon[i] + structureOrigin[0].item())
polygon.append(boundaryPolygon[i+1] + structureOrigin[1].item())
# make it a tuple
polygon = tuple(polygon)
boundaries.append(polygon)
else:
# else shape is a rectangle
left_bottom=boundary.coordinates[0]
right_top=boundary.coordinates[2]
left_bottom = boundary.coordinates[0]
right_top = boundary.coordinates[2]
# Rectangle is [leftx, bottomy, rightx, topy].
boundaryRect=[left_bottom[0],left_bottom[1],right_top[0],right_top[1]]
boundaryRect = [left_bottom[0], left_bottom[1],
right_top[0], right_top[1]]
# perform the rotation
boundaryRect=self.transformRectangle(boundaryRect,structureuVector,structurevVector)
boundaryRect = self.transformRectangle(boundaryRect,
structureuVector,
structurevVector)
# add the offset and make it a tuple
boundaryRect=(boundaryRect[0]+structureOrigin[0].item(),boundaryRect[1]+structureOrigin[1].item(),
boundaryRect[2]+structureOrigin[0].item(),boundaryRect[3]+structureOrigin[1].item())
boundaryRect = (boundaryRect[0]+structureOrigin[0].item(),
boundaryRect[1]+structureOrigin[1].item(),
boundaryRect[2]+structureOrigin[0].item(),
boundaryRect[3]+structureOrigin[1].item())
boundaries.append(boundaryRect)
return boundaries
@ -868,6 +896,17 @@ class VlsiLayout:
else:
return False
def sameLPP(lpp1, lpp2):
"""
Check if the layers and purposes are the same.
Ignore if purpose is a None.
"""
if lpp1[1] == None or lpp2[1] == None:
return lpp1[0] == lpp2[0]
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
def boundaryArea(A):
"""

View File

@ -5,13 +5,11 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import globals
import design
from math import log
import design
from tech import GDS,layer,spice,parameter
from tech import GDS, layer, spice, parameter
import utils
class dff(design.design):
"""
Memory address flip-flop
@ -19,7 +17,9 @@ class dff(design.design):
pin_names = ["D", "Q", "clk", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("dff", GDS["unit"], layer["boundary"])
(width, height) = utils.get_libcell_size("dff",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
def __init__(self, name="dff"):

View File

@ -18,16 +18,17 @@ class pin_group:
determine how pin shapes get mapped to tracks.
It is initially constructed with a single set of (touching) pins.
"""
def __init__(self, name, pin_set, router):
self.name = name
# Flag for when it is routed
self.routed = False
# Flag for when it is enclosed
self.enclosed = False
# Remove any redundant pins (i.e. contained in other pins)
irredundant_pin_set = self.remove_redundant_shapes(list(pin_set))
# This is a list because we can have a pin
# group of disconnected sets of pins
# and these are represented by separate lists
@ -39,7 +40,7 @@ class pin_group:
# These are the secondary grids that could
# or could not be part of the pin
self.secondary_grids = set()
# The corresponding set of partially blocked grids for each pin group.
# These are blockages for other nets but unblocked
# for routing this group. These are also blockages if we
@ -52,16 +53,16 @@ class pin_group:
def __str__(self):
""" override print function output """
total_string = "(pg {} ".format(self.name)
pin_string = "\n pins={}".format(self.pins)
total_string += pin_string
grids_string = "\n grids={}".format(self.grids)
total_string += grids_string
grids_string = "\n secondary={}".format(self.secondary_grids)
total_string += grids_string
if self.enclosed:
enclosure_string = "\n enclose={}".format(self.enclosures)
total_string += enclosure_string
@ -72,7 +73,7 @@ class pin_group:
def __repr__(self):
""" override repr function output """
return str(self)
def size(self):
return len(self.grids)
@ -81,7 +82,7 @@ class pin_group:
def is_routed(self):
return self.routed
def remove_redundant_shapes(self, pin_list):
"""
Remove any pin layout that is contained within another.
@ -90,7 +91,7 @@ class pin_group:
local_debug = False
if local_debug:
debug.info(0, "INITIAL: {}".format(pin_list))
# Make a copy of the list to start
new_pin_list = pin_list.copy()
@ -100,7 +101,7 @@ class pin_group:
# If we remove this pin, it can't contain other pins
if index1 in remove_indices:
continue
for index2, pin2 in enumerate(pin_list):
# Can't contain yourself,
# but compare the indices and not the pins
@ -110,7 +111,7 @@ class pin_group:
# If we already removed it, can't remove it again...
if index2 in remove_indices:
continue
if pin1.contains(pin2):
if local_debug:
debug.info(0, "{0} contains {1}".format(pin1, pin2))
@ -119,10 +120,10 @@ class pin_group:
# Remove them in decreasing order to not invalidate the indices
for i in sorted(remove_indices, reverse=True):
del new_pin_list[i]
if local_debug:
debug.info(0, "FINAL : {}".format(new_pin_list))
return new_pin_list
def compute_enclosures(self):
@ -144,11 +145,17 @@ class pin_group:
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
pin_list.append(enclosure)
# Now simplify the enclosure list
debug.check(len(pin_list) > 0,
"Did not find any enclosures.")
# Now simplify the enclosure list
new_pin_list = self.remove_redundant_shapes(pin_list)
return new_pin_list
debug.check(len(new_pin_list) > 0,
"Did not find any enclosures.")
return new_pin_list
def compute_connector(self, pin, enclosure):
"""
Compute a shape to connect the pin to the enclosure shape.
@ -200,7 +207,7 @@ class pin_group:
for shape in enclosures:
if shape.xcontains(pin):
edge_list.append(shape)
# Sort them by their bottom edge
edge_list.sort(key=lambda x: x.by(), reverse=True)
@ -233,7 +240,7 @@ class pin_group:
for shape in enclosures:
if shape.xcontains(pin):
edge_list.append(shape)
# Sort them by their upper edge
edge_list.sort(key=lambda x: x.uy())
@ -251,13 +258,13 @@ class pin_group:
# If it already overlaps, no connector needed
if bottom_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, bottom_item)
return p
def find_left_connector(self, pin, enclosures):
"""
"""
Find the enclosure that is to the left of the pin
and make a connector to it's right edge.
"""
@ -266,14 +273,14 @@ class pin_group:
for shape in enclosures:
if shape.ycontains(pin):
edge_list.append(shape)
# Sort them by their right edge
edge_list.sort(key=lambda x: x.rx())
# Find the right edge that is to the pin's left edge
left_item = None
for item in edge_list:
if item.rx()<=pin.lx():
if item.rx() <= pin.lx():
left_item = item
else:
break
@ -284,13 +291,13 @@ class pin_group:
# If it already overlaps, no connector needed
if left_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, left_item)
return p
def find_right_connector(self, pin, enclosures):
"""
"""
Find the enclosure that is to the right of the pin
and make a connector to it's left edge.
"""
@ -299,7 +306,7 @@ class pin_group:
for shape in enclosures:
if shape.ycontains(pin):
edge_list.append(shape)
# Sort them by their right edge
edge_list.sort(key=lambda x: x.lx(), reverse=True)
@ -317,11 +324,11 @@ class pin_group:
# If it already overlaps, no connector needed
if right_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, right_item)
return p
def find_smallest_connector(self, pin_list, shape_list):
"""
Compute all of the connectors between the overlapping
@ -334,7 +341,7 @@ class pin_group:
new_enclosure = self.compute_connector(pin, enclosure)
if not smallest or new_enclosure.area() < smallest.area():
smallest = new_enclosure
return smallest
def find_smallest_overlapping(self, pin_list, shape_list):
@ -350,7 +357,7 @@ class pin_group:
# overlap_length = pin.overlap_length(overlap_shape)
if not smallest_shape or overlap_shape.area() < smallest_shape.area():
smallest_shape = overlap_shape
return smallest_shape
def find_smallest_overlapping_pin(self, pin, shape_list):
@ -369,9 +376,9 @@ class pin_group:
if overlap_length > min_width:
if not smallest_shape or other.area() < smallest_shape.area():
smallest_shape = other
return smallest_shape
def overlap_any_shape(self, pin_list, shape_list):
"""
Does the given pin overlap any of the shapes in the pin list.
@ -391,7 +398,7 @@ class pin_group:
for pin in pin_list:
if pin.area() > biggest.area():
biggest = pin
return pin
def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST):
@ -406,8 +413,7 @@ class pin_group:
offset2 = direction.get_offset(dir2)
# We may have started with an empty set
if not self.grids:
return None
debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.")
# Start with the ll and make the widest row
row = [ll]
@ -436,7 +442,7 @@ class pin_group:
# Add a shape from ll to ur
ur = row[-1]
return (ll, ur)
def enclose_pin(self):
"""
If there is one set of connected pin shapes,
@ -445,13 +451,13 @@ class pin_group:
If there is not, it simply returns all the enclosures.
"""
self.enclosed = True
# Compute the enclosure pin_layout list of the set of tracks
self.enclosures = self.compute_enclosures()
# Find a connector to every pin and add it to the enclosures
for pin in self.pins:
# If it is contained, it won't need a connector
if pin.contained_by_any(self.enclosures):
continue
@ -469,7 +475,7 @@ class pin_group:
right_connector,
above_connector,
below_connector]
filtered_list = list(filter(lambda x: x!=None, connector_list))
filtered_list = list(filter(lambda x: x != None, connector_list))
if (len(filtered_list) > 0):
import copy
bbox_connector = copy.copy(pin)
@ -487,8 +493,9 @@ class pin_group:
debug.error("Could not find a connector for {} with {}".format(self.pins,
self.enclosures))
self.router.write_debug_gds("no_connector.gds")
import pdb; pdb.set_trace()
self.enclosures.append(connector)
# At this point, the pins are overlapping,
# but there might be more than one!
overlap_set = set()
@ -505,12 +512,11 @@ class pin_group:
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
enclosure)
self.grids.update(sufficient)
debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
self.pins,
self.grids,
self.enclosures))
debug.info(3, "Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
self.pins,
self.grids,
self.enclosures))
def transitive_overlap(self, shape, shape_list):
"""
@ -532,22 +538,22 @@ class pin_group:
# Remove the original shape
connected_set.remove(shape)
# if len(connected_set)<len(shape_list):
# import pprint
# print("S: ",shape)
# pprint.pprint(shape_list)
# pprint.pprint(connected_set)
return connected_set
def add_enclosure(self, cell):
"""
Add the enclosure shape to the given cell.
"""
for enclosure in self.enclosures:
debug.info(2, "Adding enclosure {0} {1}".format(self.name,
enclosure))
enclosure))
cell.add_rect(layer=enclosure.layer,
offset=enclosure.ll(),
width=enclosure.width(),
@ -566,9 +572,9 @@ class pin_group:
# If we aren't completely enclosed, we are on the perimeter
if neighbor_count < 4:
perimeter_set.add(g1)
return perimeter_set
def adjacent(self, other):
"""
Chck if the two pin groups have at least one adjacent pin grid.
@ -594,11 +600,11 @@ class pin_group:
adj_grids.add(g1)
return adj_grids
def convert_pin(self):
"""
Convert the list of pin shapes into sets of routing grids.
The secondary set of grids are "optional" pin shapes that could be
The secondary set of grids are "optional" pin shapes that
should be either blocked or part of the pin.
"""
pin_set = set()
@ -608,11 +614,11 @@ class pin_group:
for pin in self.pins:
debug.info(2, " Converting {0}".format(pin))
# Determine which tracks the pin overlaps
(sufficient, insufficient)=self.router.convert_pin_to_tracks(self.name,
pin)
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
pin)
pin_set.update(sufficient)
partial_set.update(insufficient)
# Blockages will be a super-set of pins since
# it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin)
@ -632,20 +638,20 @@ class pin_group:
if len(shared_set) > 0:
debug.info(2, "Removing blocks {}".format(shared_set))
blockage_set.difference_update(shared_set)
# At least one of the groups must have some valid tracks
if (len(pin_set) == 0 and len(partial_set) == 0 and len(blockage_set) == 0):
# debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
for pin in self.pins:
debug.warning(" Expanding conversion {0}".format(pin))
# Determine which tracks the pin overlaps
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name,
pin,
expansion=1)
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
pin,
expansion=1)
pin_set.update(sufficient)
partial_set.update(insufficient)
if len(pin_set) == 0 and len(partial_set) == 0:
debug.error("Unable to find unblocked pin {} {}".format(self.name,
self.pins))
@ -653,11 +659,12 @@ class pin_group:
# Consider all the grids that would be blocked
self.grids = pin_set | partial_set
if len(self.grids) < 0:
debug.error("Did not find any unblocked grids: {}".format(str(self.pins)))
self.router.write_debug_gds("blocked_pin.gds")
# Remember the secondary grids for removing adjacent pins
self.secondary_grids = partial_set
debug.info(2, " pins {}".format(self.grids))
debug.info(2, " secondary {}".format(self.secondary_grids))

View File

@ -27,11 +27,10 @@ class router(router_tech):
route on a given layer. This is limited to two layer routes.
It populates blockages on a grid class.
"""
def __init__(self, layers, design, gds_filename=None, rail_track_width=1):
"""
This will instantiate a copy of the gds file or the module at (0,0) and
route on top of this. The blockages from the gds/module will be
route on top of this. The blockages from the gds/module will be
considered.
"""
router_tech.__init__(self, layers, rail_track_width)
@ -84,7 +83,7 @@ class router(router_tech):
self.boundary = self.layout.measureBoundary(self.top_name)
# These must be un-indexed to get rid of the matrix type
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
def clear_pins(self):
"""
@ -93,7 +92,7 @@ class router(router_tech):
"""
self.pins = {}
self.all_pins = set()
self.pin_groups = {}
self.pin_groups = {}
# DO NOT clear the blockages as these don't change
self.rg.reinit()
@ -112,7 +111,7 @@ class router(router_tech):
"""
Retrieve the pin shapes on metal 3 from the layout.
"""
debug.info(2, "Retrieving pins for {}.".format(pin_name))
debug.info(2, "Retrieving pins for {}.".format(pin_name))
shape_list = self.layout.getAllPinShapes(str(pin_name))
pin_set = set()
for shape in shape_list:
@ -140,13 +139,13 @@ class router(router_tech):
This doesn't consider whether the obstacles will be pins or not.
They get reset later if they are not actually a blockage.
"""
debug.info(1, "Finding blockages.")
for layer in [self.vert_layer_number, self.horiz_layer_number]:
self.retrieve_blockages(layer)
debug.info(1, "Finding blockages.")
for lpp in [self.vert_lpp, self.horiz_lpp]:
self.retrieve_blockages(lpp)
def find_pins_and_blockages(self, pin_list):
"""
Find the pins and blockages in the design
Find the pins and blockages in the design
"""
# This finds the pin shapes and sorts them into "groups" that
# are connected. This must come before the blockages, so we
@ -447,7 +446,7 @@ class router(router_tech):
"""
# Inflate the blockage by half a spacing rule
[ll, ur] = self.convert_blockage_to_tracks(blockage.inflate())
zlayer = self.get_zindex(blockage.lpp[0])
zlayer = self.get_zindex(blockage.lpp)
blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer)
return blockage_tracks
@ -459,19 +458,19 @@ class router(router_tech):
blockage_list = self.convert_blockage(blockage)
self.blocked_grids.update(blockage_list)
def retrieve_blockages(self, layer_num):
def retrieve_blockages(self, lpp):
"""
Recursive find boundaries as blockages to the routing grid.
"""
shapes = self.layout.getAllShapes(layer_num)
shapes = self.layout.getAllShapes(lpp)
for boundary in shapes:
ll = vector(boundary[0], boundary[1])
ur = vector(boundary[2], boundary[3])
rect = [ll, ur]
new_pin = pin_layout("blockage{}".format(len(self.blockages)),
rect,
layer_num)
lpp)
# If there is a rectangle that is the same in the pins,
# it isn't a blockage!
@ -529,7 +528,7 @@ class router(router_tech):
sufficient_list = set()
insufficient_list = set()
zindex = self.get_zindex(pin.lpp[0])
zindex = self.get_zindex(pin.lpp)
for x in range(int(ll[0]) + expansion, int(ur[0]) + 1 + expansion):
for y in range(int(ll[1] + expansion), int(ur[1]) + 1 + expansion):
(full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin,
@ -622,7 +621,6 @@ class router(router_tech):
"""
Return all tracks that an inflated pin overlaps
"""
# This is using the full track shape rather
# than a single track pin shape
# because we will later patch a connector if there isn't overlap.
@ -807,8 +805,8 @@ class router(router_tech):
def enclose_pins(self):
"""
This will find the biggest rectangle enclosing some grid squares and
put a rectangle over it. It does not enclose grid squares that are blocked
by other shapes.
put a rectangle over it. It does not enclose grid squares
that are blocked by other shapes.
"""
for pin_name in self.pin_groups:
debug.info(1, "Enclosing pins for {}".format(pin_name))

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from tech import drc,layer
from tech import drc, layer
from contact import contact
from pin_group import pin_group
from vector import vector
import debug
import math
class router_tech:
"""
This is a class to hold the router tech constants.
@ -25,9 +25,9 @@ class router_tech:
self.layers = layers
self.rail_track_width = rail_track_width
if len(self.layers)==1:
if len(self.layers) == 1:
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
self.horiz_layer_number = self.vert_layer_number = layer[self.layers[0]]
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
@ -40,8 +40,8 @@ class router_tech:
via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width,via_connect.height)
self.horiz_layer_number = layer[self.horiz_layer_name]
self.vert_layer_number = layer[self.vert_layer_name]
self.horiz_lpp = layer[self.horiz_layer_name]
self.vert_lpp = layer[self.vert_layer_name]
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
@ -68,8 +68,18 @@ class router_tech:
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_wire, 1, self.track_wire]
def get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number:
def same_lpp(self, lpp1, lpp2):
"""
Check if the layers and purposes are the same.
Ignore if purpose is a None.
"""
if lpp1[1] == None or lpp2[1] == None:
return lpp1[0] == lpp2[0]
return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1]
def get_zindex(self, lpp):
if self.same_lpp(lpp, self.horiz_lpp):
return 0
else:
return 1

View File

@ -55,7 +55,7 @@ class supply_grid_router(router):
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"):
"""
"""
Add power supply rails and connect all pins to these rails.
"""
debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
@ -75,7 +75,6 @@ class supply_grid_router(router):
start_time = datetime.now()
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
print_time("Finding pins and blockages",datetime.now(), start_time, 3)
# Add the supply rails in a mesh network and connect H/V with vias
start_time = datetime.now()
# Block everything

View File

@ -67,7 +67,6 @@ layer["via9"] = (28, 0)
layer["metal10"] = (29, 0)
layer["text"] = (239, 0)
layer["boundary"]= (239, 0)
#layer["blockage"]= (239, 0)
###################################################
##END GDS Layer Map

View File

@ -54,7 +54,6 @@ layer["via3"] = (30, 0)
layer["metal4"] = (31, 0)
layer["text"] = (63, 0)
layer["boundary"] = (63, 0)
#layer["blockage"] = (83, 0)
###################################################
##END GDS Layer Map