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 # snap the rect to the grid
self.rect = [x.snap_to_grid() for x in self.rect] self.rect = [x.snap_to_grid() for x in self.rect]
debug.check(self.width() > 0,"Zero width pin.") debug.check(self.width() > 0, "Zero width pin.")
debug.check(self.height() > 0,"Zero height pin.") debug.check(self.height() > 0, "Zero height pin.")
# if it's a string, use the name # if it's a string, use the name
if type(layer_name_pp) == str: if type(layer_name_pp) == str:
@ -37,7 +37,7 @@ class pin_layout:
# else it is required to be a lpp # else it is required to be a lpp
else: else:
for (layer_name, lpp) in layer.items(): 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 self.layer = layer_name
break break
else: else:
@ -78,7 +78,7 @@ class pin_layout:
def __eq__(self, other): def __eq__(self, other):
""" Check if these are the same pins for duplicate checks """ """ Check if these are the same pins for duplicate checks """
if isinstance(other, self.__class__): 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: else:
return False return False
@ -177,7 +177,7 @@ class pin_layout:
return True return True
# Can only overlap on the same layer # Can only overlap on the same layer
if self.layer != other.layer: if not self.same_lpp(self.lpp, other.lpp):
return False return False
if not self.xcontains(other): if not self.xcontains(other):
@ -198,7 +198,7 @@ class pin_layout:
def overlaps(self, other): def overlaps(self, other):
""" Check if a shape overlaps with a rectangle """ """ Check if a shape overlaps with a rectangle """
# Can only overlap on the same layer # Can only overlap on the same layer
if self.layer != other.layer: if not self.same_lpp(self.lpp, other.lpp):
return False return False
x_overlaps = self.xoverlaps(other) x_overlaps = self.xoverlaps(other)
@ -506,3 +506,13 @@ class pin_layout:
return r return r
return None 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import os
import gdsMill import gdsMill
import tech import tech
import math import math
@ -16,6 +15,7 @@ from pin_layout import pin_layout
OPTS = globals.OPTS OPTS = globals.OPTS
def ceil(decimal): def ceil(decimal):
""" """
Performs a ceiling function on the decimal place specified by the DRC grid. Performs a ceiling function on the decimal place specified by the DRC grid.
@ -23,6 +23,7 @@ def ceil(decimal):
grid = tech.drc["grid"] grid = tech.drc["grid"]
return math.ceil(decimal * 1 / grid) / (1 / grid) return math.ceil(decimal * 1 / grid) / (1 / grid)
def round_to_grid(number): def round_to_grid(number):
""" """
Rounds an arbitrary number to the grid. Rounds an arbitrary number to the grid.
@ -33,17 +34,22 @@ def round_to_grid(number):
number_off = number_grid * grid number_off = number_grid * grid
return number_off return number_off
def snap_to_grid(offset): def snap_to_grid(offset):
""" """
Changes the coodrinate to match the grid settings 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): def pin_center(boundary):
""" """
This returns the center of a pin shape in the vlsiLayout border format. 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): def auto_measure_libcell(pin_list, name, units, lpp):
""" """
@ -57,35 +63,34 @@ def auto_measure_libcell(pin_list, name, units, lpp):
cell = {} cell = {}
measure_result = cell_vlsi.getLayoutBorder(lpp[0]) measure_result = cell_vlsi.getLayoutBorder(lpp[0])
if measure_result == None: if measure_result:
measure_result = cell_vlsi.measureSize(name) measure_result = cell_vlsi.measureSize(name)
[cell["width"], cell["height"]] = measure_result [cell["width"], cell["height"]] = measure_result
for pin in pin_list: 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) cell[str(pin)] = pin_center(boundary)
return cell return cell
def get_gds_size(name, gds_filename, units, lpp): def get_gds_size(name, gds_filename, units, lpp):
""" """
Open a GDS file and return the size from either the Open a GDS file and return the size from either the
bounding box or a border layer. 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) cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi) reader = gdsMill.Gds2reader(cell_vlsi)
reader.loadFromFile(gds_filename) reader.loadFromFile(gds_filename)
cell = {}
measure_result = cell_vlsi.getLayoutBorder(lpp) measure_result = cell_vlsi.getLayoutBorder(lpp)
if measure_result == None: if not measure_result:
debug.info(2,"Layout border failed. Trying to measure size for {}".format(name)) debug.info(2, "Layout border failed. Trying to measure size for {}".format(name))
measure_result = cell_vlsi.measureSize(name) measure_result = cell_vlsi.measureSize(name)
# returns width,height # returns width,height
return measure_result return measure_result
def get_libcell_size(name, units, lpp): def get_libcell_size(name, units, lpp):
""" """
Open a GDS file and return the library cell size from either the 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 = {} cell = {}
for pin_name in pin_names: for pin_name in pin_names:
cell[str(pin_name)]=[] cell[str(pin_name)] = []
pin_list=cell_vlsi.getPinShape(str(pin_name)) pin_list = cell_vlsi.getPinShape(str(pin_name))
for pin_shape in pin_list: for pin_shape in pin_list:
(lpp,boundary)=pin_shape (lpp, boundary) = pin_shape
rect=[vector(boundary[0],boundary[1]),vector(boundary[2],boundary[3])] rect = [vector(boundary[0], boundary[1]),
# this is a list because other cells/designs may have must-connect pins 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)) cell[str(pin_name)].append(pin_layout(pin_name, rect, lpp))
return cell return cell
def get_libcell_pins(pin_list, name, units): 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. 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)) os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0: if globals.OPTS.debug_level > 0:
import pdb; pdb.set_trace() import pdb
else: pdb.set_trace()
assert 0 assert 0
def error(str, return_value=0): def error(str, return_value=0):
@ -41,9 +41,9 @@ def error(str, return_value=0):
os.path.basename(filename), line_number, str)) os.path.basename(filename), line_number, str))
if globals.OPTS.debug_level > 0: if globals.OPTS.debug_level > 0:
import pdb; pdb.set_trace() import pdb
else: pdb.set_trace()
assert return_value == 0 assert return_value == 0
def warning(str): def warning(str):

View File

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

View File

@ -5,13 +5,11 @@
# (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 globals
import design import design
from math import log from tech import GDS, layer, spice, parameter
import design
from tech import GDS,layer,spice,parameter
import utils import utils
class dff(design.design): class dff(design.design):
""" """
Memory address flip-flop Memory address flip-flop
@ -19,7 +17,9 @@ class dff(design.design):
pin_names = ["D", "Q", "clk", "vdd", "gnd"] pin_names = ["D", "Q", "clk", "vdd", "gnd"]
type_list = ["INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] 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"]) pin_map = utils.get_libcell_pins(pin_names, "dff", GDS["unit"])
def __init__(self, name="dff"): def __init__(self, name="dff"):

View File

@ -18,6 +18,7 @@ class pin_group:
determine how pin shapes get mapped to tracks. determine how pin shapes get mapped to tracks.
It is initially constructed with a single set of (touching) pins. It is initially constructed with a single set of (touching) pins.
""" """
def __init__(self, name, pin_set, router): def __init__(self, name, pin_set, router):
self.name = name self.name = name
# Flag for when it is routed # Flag for when it is routed
@ -144,9 +145,15 @@ class pin_group:
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z) enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
pin_list.append(enclosure) pin_list.append(enclosure)
debug.check(len(pin_list) > 0,
"Did not find any enclosures.")
# Now simplify the enclosure list # Now simplify the enclosure list
new_pin_list = self.remove_redundant_shapes(pin_list) new_pin_list = self.remove_redundant_shapes(pin_list)
debug.check(len(new_pin_list) > 0,
"Did not find any enclosures.")
return new_pin_list return new_pin_list
def compute_connector(self, pin, enclosure): def compute_connector(self, pin, enclosure):
@ -273,7 +280,7 @@ class pin_group:
# Find the right edge that is to the pin's left edge # Find the right edge that is to the pin's left edge
left_item = None left_item = None
for item in edge_list: for item in edge_list:
if item.rx()<=pin.lx(): if item.rx() <= pin.lx():
left_item = item left_item = item
else: else:
break break
@ -406,8 +413,7 @@ class pin_group:
offset2 = direction.get_offset(dir2) offset2 = direction.get_offset(dir2)
# We may have started with an empty set # We may have started with an empty set
if not self.grids: debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.")
return None
# Start with the ll and make the widest row # Start with the ll and make the widest row
row = [ll] row = [ll]
@ -469,7 +475,7 @@ class pin_group:
right_connector, right_connector,
above_connector, above_connector,
below_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): if (len(filtered_list) > 0):
import copy import copy
bbox_connector = copy.copy(pin) bbox_connector = copy.copy(pin)
@ -487,6 +493,7 @@ class pin_group:
debug.error("Could not find a connector for {} with {}".format(self.pins, debug.error("Could not find a connector for {} with {}".format(self.pins,
self.enclosures)) self.enclosures))
self.router.write_debug_gds("no_connector.gds") self.router.write_debug_gds("no_connector.gds")
import pdb; pdb.set_trace()
self.enclosures.append(connector) self.enclosures.append(connector)
# At this point, the pins are overlapping, # At this point, the pins are overlapping,
@ -506,11 +513,10 @@ class pin_group:
enclosure) enclosure)
self.grids.update(sufficient) self.grids.update(sufficient)
debug.info(3, "Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, self.pins,
self.pins, self.grids,
self.grids, self.enclosures))
self.enclosures))
def transitive_overlap(self, shape, shape_list): def transitive_overlap(self, shape, shape_list):
""" """
@ -598,7 +604,7 @@ class pin_group:
def convert_pin(self): def convert_pin(self):
""" """
Convert the list of pin shapes into sets of routing grids. 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. should be either blocked or part of the pin.
""" """
pin_set = set() pin_set = set()
@ -608,8 +614,8 @@ class pin_group:
for pin in self.pins: for pin in self.pins:
debug.info(2, " Converting {0}".format(pin)) debug.info(2, " Converting {0}".format(pin))
# Determine which tracks the pin overlaps # Determine which tracks the pin overlaps
(sufficient, insufficient)=self.router.convert_pin_to_tracks(self.name, (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
pin) pin)
pin_set.update(sufficient) pin_set.update(sufficient)
partial_set.update(insufficient) partial_set.update(insufficient)
@ -640,9 +646,9 @@ class pin_group:
for pin in self.pins: for pin in self.pins:
debug.warning(" Expanding conversion {0}".format(pin)) debug.warning(" Expanding conversion {0}".format(pin))
# Determine which tracks the pin overlaps # Determine which tracks the pin overlaps
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
pin, pin,
expansion=1) expansion=1)
pin_set.update(sufficient) pin_set.update(sufficient)
partial_set.update(insufficient) partial_set.update(insufficient)
@ -653,11 +659,12 @@ class pin_group:
# Consider all the grids that would be blocked # Consider all the grids that would be blocked
self.grids = pin_set | partial_set 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 # Remember the secondary grids for removing adjacent pins
self.secondary_grids = partial_set self.secondary_grids = partial_set
debug.info(2, " pins {}".format(self.grids)) debug.info(2, " pins {}".format(self.grids))
debug.info(2, " secondary {}".format(self.secondary_grids)) debug.info(2, " secondary {}".format(self.secondary_grids))

View File

@ -27,7 +27,6 @@ class router(router_tech):
route on a given layer. This is limited to two layer routes. route on a given layer. This is limited to two layer routes.
It populates blockages on a grid class. It populates blockages on a grid class.
""" """
def __init__(self, layers, design, gds_filename=None, rail_track_width=1): 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 This will instantiate a copy of the gds file or the module at (0,0) and
@ -141,8 +140,8 @@ class router(router_tech):
They get reset later if they are not actually a blockage. They get reset later if they are not actually a blockage.
""" """
debug.info(1, "Finding blockages.") debug.info(1, "Finding blockages.")
for layer in [self.vert_layer_number, self.horiz_layer_number]: for lpp in [self.vert_lpp, self.horiz_lpp]:
self.retrieve_blockages(layer) self.retrieve_blockages(lpp)
def find_pins_and_blockages(self, pin_list): def find_pins_and_blockages(self, pin_list):
""" """
@ -447,7 +446,7 @@ class router(router_tech):
""" """
# Inflate the blockage by half a spacing rule # Inflate the blockage by half a spacing rule
[ll, ur] = self.convert_blockage_to_tracks(blockage.inflate()) [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) blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer)
return blockage_tracks return blockage_tracks
@ -459,19 +458,19 @@ class router(router_tech):
blockage_list = self.convert_blockage(blockage) blockage_list = self.convert_blockage(blockage)
self.blocked_grids.update(blockage_list) 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. Recursive find boundaries as blockages to the routing grid.
""" """
shapes = self.layout.getAllShapes(layer_num) shapes = self.layout.getAllShapes(lpp)
for boundary in shapes: for boundary in shapes:
ll = vector(boundary[0], boundary[1]) ll = vector(boundary[0], boundary[1])
ur = vector(boundary[2], boundary[3]) ur = vector(boundary[2], boundary[3])
rect = [ll, ur] rect = [ll, ur]
new_pin = pin_layout("blockage{}".format(len(self.blockages)), new_pin = pin_layout("blockage{}".format(len(self.blockages)),
rect, rect,
layer_num) lpp)
# If there is a rectangle that is the same in the pins, # If there is a rectangle that is the same in the pins,
# it isn't a blockage! # it isn't a blockage!
@ -529,7 +528,7 @@ class router(router_tech):
sufficient_list = set() sufficient_list = set()
insufficient_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 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): for y in range(int(ll[1] + expansion), int(ur[1]) + 1 + expansion):
(full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, (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 Return all tracks that an inflated pin overlaps
""" """
# This is using the full track shape rather # This is using the full track shape rather
# than a single track pin shape # than a single track pin shape
# because we will later patch a connector if there isn't overlap. # because we will later patch a connector if there isn't overlap.
@ -807,8 +805,8 @@ class router(router_tech):
def enclose_pins(self): def enclose_pins(self):
""" """
This will find the biggest rectangle enclosing some grid squares and 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 put a rectangle over it. It does not enclose grid squares
by other shapes. that are blocked by other shapes.
""" """
for pin_name in self.pin_groups: for pin_name in self.pin_groups:
debug.info(1, "Enclosing pins for {}".format(pin_name)) debug.info(1, "Enclosing pins for {}".format(pin_name))

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
from tech import drc,layer from tech import drc, layer
from contact import contact from contact import contact
from pin_group import pin_group
from vector import vector from vector import vector
import debug import debug
import math import math
class router_tech: class router_tech:
""" """
This is a class to hold the router tech constants. This is a class to hold the router tech constants.
@ -25,9 +25,9 @@ class router_tech:
self.layers = layers self.layers = layers
self.rail_track_width = rail_track_width 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_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.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) (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)) via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width,via_connect.height) max_via_size = max(via_connect.width,via_connect.height)
self.horiz_layer_number = layer[self.horiz_layer_name] self.horiz_lpp = layer[self.horiz_layer_name]
self.vert_layer_number = layer[self.vert_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.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) (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) # 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] self.layer_widths = [self.track_wire, 1, self.track_wire]
def get_zindex(self,layer_num): def same_lpp(self, lpp1, lpp2):
if layer_num==self.horiz_layer_number: """
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 return 0
else: else:
return 1 return 1

View File

@ -75,7 +75,6 @@ class supply_grid_router(router):
start_time = datetime.now() start_time = datetime.now()
self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
print_time("Finding pins and blockages",datetime.now(), start_time, 3) 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 # Add the supply rails in a mesh network and connect H/V with vias
start_time = datetime.now() start_time = datetime.now()
# Block everything # Block everything

View File

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

View File

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