PEP8 formatting

This commit is contained in:
Matthew Guthaus 2019-11-07 16:33:13 +00:00
parent a2422cc8d4
commit 0dea153919
2 changed files with 468 additions and 419 deletions

View File

@ -7,12 +7,10 @@
# #
from direction import direction from direction import direction
from pin_layout import pin_layout from pin_layout import pin_layout
from vector3d import vector3d
from vector import vector from vector import vector
import grid_utils
from tech import drc
import debug import debug
class pin_group: class pin_group:
""" """
A class to represent a group of rectangular design pin. A class to represent a group of rectangular design pin.
@ -30,19 +28,22 @@ class pin_group:
# Remove any redundant pins (i.e. contained in other pins) # Remove any redundant pins (i.e. contained in other pins)
irredundant_pin_set = self.remove_redundant_shapes(list(pin_set)) 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 # This is a list because we can have a pin
# group of disconnected sets of pins
# and these are represented by separate lists # and these are represented by separate lists
self.pins = set(irredundant_pin_set) self.pins = set(irredundant_pin_set)
self.router = router self.router = router
# These are the corresponding pin grids for each pin group. # These are the corresponding pin grids for each pin group.
self.grids = set() self.grids = set()
# These are the secondary grids that could or could not be part of the pin # These are the secondary grids that could
# or could not be part of the pin
self.secondary_grids = set() self.secondary_grids = set()
# The corresponding set of partially blocked grids for each pin group. # 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 blockages for other nets but unblocked
# These are also blockages if we used a simple enclosure to route to a rail. # for routing this group. These are also blockages if we
# used a simple enclosure to route to a rail.
self.blockages = set() self.blockages = set()
# This is a set of pin_layout shapes to cover the grids # This is a set of pin_layout shapes to cover the grids
@ -101,7 +102,8 @@ class pin_group:
continue continue
for index2, pin2 in enumerate(pin_list): for index2, pin2 in enumerate(pin_list):
# Can't contain yourself, but compare the indices and not the pins # Can't contain yourself,
# but compare the indices and not the pins
# so you can remove duplicate copies. # so you can remove duplicate copies.
if index1 == index2: if index1 == index2:
continue continue
@ -130,15 +132,18 @@ class pin_group:
# Enumerate every possible enclosure # Enumerate every possible enclosure
pin_list = [] pin_list = []
for seed in self.grids: for seed in self.grids:
(ll, ur) = self.enclose_pin_grids(seed, direction.NORTH, direction.EAST) (ll, ur) = self.enclose_pin_grids(seed,
direction.NORTH,
direction.EAST)
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)
(ll, ur) = self.enclose_pin_grids(seed, direction.EAST, direction.NORTH) (ll, ur) = self.enclose_pin_grids(seed,
direction.EAST,
direction.NORTH)
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)
# 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)
@ -154,7 +159,7 @@ class pin_group:
plc = pin.lc() plc = pin.lc()
prc = pin.rc() prc = pin.rc()
elc = enclosure.lc() elc = enclosure.lc()
erc = enclosure.rc() # erc = enclosure.rc()
ymin = min(plc.y, elc.y) ymin = min(plc.y, elc.y)
ymax = max(plc.y, elc.y) ymax = max(plc.y, elc.y)
ll = vector(plc.x, ymin) ll = vector(plc.x, ymin)
@ -164,7 +169,7 @@ class pin_group:
pbc = pin.bc() pbc = pin.bc()
puc = pin.uc() puc = pin.uc()
ebc = enclosure.bc() ebc = enclosure.bc()
euc = enclosure.uc() # euc = enclosure.uc()
xmin = min(pbc.x, ebc.x) xmin = min(pbc.x, ebc.x)
xmax = max(pbc.x, ebc.x) xmax = max(pbc.x, ebc.x)
ll = vector(xmin, pbc.y) ll = vector(xmin, pbc.y)
@ -208,7 +213,7 @@ class pin_group:
break break
# There was nothing # There was nothing
if above_item==None: if not above_item:
return None return None
# If it already overlaps, no connector needed # If it already overlaps, no connector needed
if above_item.overlaps(pin): if above_item.overlaps(pin):
@ -241,7 +246,7 @@ class pin_group:
break break
# There was nothing to the left # There was nothing to the left
if bottom_item==None: if not bottom_item:
return None return None
# If it already overlaps, no connector needed # If it already overlaps, no connector needed
if bottom_item.overlaps(pin): if bottom_item.overlaps(pin):
@ -274,7 +279,7 @@ class pin_group:
break break
# There was nothing to the left # There was nothing to the left
if left_item==None: if not left_item:
return None return None
# If it already overlaps, no connector needed # If it already overlaps, no connector needed
if left_item.overlaps(pin): if left_item.overlaps(pin):
@ -307,7 +312,7 @@ class pin_group:
break break
# There was nothing to the right # There was nothing to the right
if right_item==None: if not right_item:
return None return None
# If it already overlaps, no connector needed # If it already overlaps, no connector needed
if right_item.overlaps(pin): if right_item.overlaps(pin):
@ -319,14 +324,15 @@ class pin_group:
def find_smallest_connector(self, pin_list, shape_list): def find_smallest_connector(self, pin_list, shape_list):
""" """
Compute all of the connectors between the overlapping pins and enclosure shape list.. Compute all of the connectors between the overlapping
pins and enclosure shape list.
Return the smallest. Return the smallest.
""" """
smallest = None smallest = None
for pin in pin_list: for pin in pin_list:
for enclosure in shape_list: for enclosure in shape_list:
new_enclosure = self.compute_connector(pin, enclosure) new_enclosure = self.compute_connector(pin, enclosure)
if smallest == None or new_enclosure.area()<smallest.area(): if not smallest or new_enclosure.area() < smallest.area():
smallest = new_enclosure smallest = new_enclosure
return smallest return smallest
@ -341,13 +347,12 @@ class pin_group:
for pin in pin_list: for pin in pin_list:
overlap_shape = self.find_smallest_overlapping_pin(pin, shape_list) overlap_shape = self.find_smallest_overlapping_pin(pin, shape_list)
if overlap_shape: if overlap_shape:
overlap_length = pin.overlap_length(overlap_shape) # overlap_length = pin.overlap_length(overlap_shape)
if smallest_shape == None or overlap_shape.area()<smallest_shape.area(): if not smallest_shape or overlap_shape.area() < smallest_shape.area():
smallest_shape = overlap_shape smallest_shape = overlap_shape
return smallest_shape return smallest_shape
def find_smallest_overlapping_pin(self, pin, shape_list): def find_smallest_overlapping_pin(self, pin, shape_list):
""" """
Find the smallest area shape in shape_list that overlaps with any Find the smallest area shape in shape_list that overlaps with any
@ -362,7 +367,7 @@ class pin_group:
for other in shape_list: for other in shape_list:
overlap_length = pin.overlap_length(other) overlap_length = pin.overlap_length(other)
if overlap_length > min_width: if overlap_length > min_width:
if smallest_shape == None or other.area()<smallest_shape.area(): if not smallest_shape or other.area() < smallest_shape.area():
smallest_shape = other smallest_shape = other
return smallest_shape return smallest_shape
@ -378,7 +383,6 @@ class pin_group:
return False return False
def max_pin_layout(self, pin_list): def max_pin_layout(self, pin_list):
""" """
Return the max area pin_layout Return the max area pin_layout
@ -433,11 +437,11 @@ class pin_group:
ur = row[-1] ur = row[-1]
return (ll, ur) return (ll, ur)
def enclose_pin(self): def enclose_pin(self):
""" """
If there is one set of connected pin shapes, If there is one set of connected pin shapes,
this will find the smallest rectangle enclosure that overlaps with any pin. this will find the smallest rectangle enclosure that
overlaps with any pin.
If there is not, it simply returns all the enclosures. If there is not, it simply returns all the enclosures.
""" """
self.enclosed = True self.enclosed = True
@ -453,40 +457,53 @@ class pin_group:
continue continue
# Find a connector in the cardinal directions # Find a connector in the cardinal directions
# If there is overlap, but it isn't contained, these could all be None # If there is overlap, but it isn't contained,
# These could also be none if the pin is diagonal from the enclosure # these could all be None
# These could also be none if the pin is
# diagonal from the enclosure
left_connector = self.find_left_connector(pin, self.enclosures) left_connector = self.find_left_connector(pin, self.enclosures)
right_connector = self.find_right_connector(pin, self.enclosures) right_connector = self.find_right_connector(pin, self.enclosures)
above_connector = self.find_above_connector(pin, self.enclosures) above_connector = self.find_above_connector(pin, self.enclosures)
below_connector = self.find_below_connector(pin, self.enclosures) below_connector = self.find_below_connector(pin, self.enclosures)
connector_list = [left_connector, right_connector, above_connector, below_connector] connector_list = [left_connector,
filtered_list = list(filter(lambda x: x!=None, connector_list)) right_connector,
above_connector,
below_connector]
filtered_list = list(filter(lambda x: not x, 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)
bbox_connector.bbox(filtered_list) bbox_connector.bbox(filtered_list)
self.enclosures.append(bbox_connector) self.enclosures.append(bbox_connector)
# Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector. # Now, make sure each pin touches an enclosure.
# This could only happen when there was no enclosure in any cardinal direction from a pin # If not, add another (diagonal) connector.
# This could only happen when there was no enclosure
# in any cardinal direction from a pin
if not self.overlap_any_shape(self.pins, self.enclosures): if not self.overlap_any_shape(self.pins, self.enclosures):
connector = self.find_smallest_connector(self.pins, self.enclosures) connector = self.find_smallest_connector(self.pins,
if connector==None: self.enclosures)
debug.error("Could not find a connector for {} with {}".format(self.pins, self.enclosures)) if not connector:
debug.error("Could not find a connector for {} with {}".format(self.pins,
self.enclosures))
self.router.write_debug_gds("no_connector.gds") self.router.write_debug_gds("no_connector.gds")
self.enclosures.append(connector) self.enclosures.append(connector)
# At this point, the pins are overlapping, but there might be more than one! # At this point, the pins are overlapping,
# but there might be more than one!
overlap_set = set() overlap_set = set()
for pin in self.pins: for pin in self.pins:
overlap_set.update(self.transitive_overlap(pin, self.enclosures)) overlap_set.update(self.transitive_overlap(pin, self.enclosures))
# Use the new enclosures and recompute the grids that correspond to them # Use the new enclosures and recompute the grids
# that correspond to them
if len(overlap_set) < len(self.enclosures): if len(overlap_set) < len(self.enclosures):
self.enclosures = overlap_set self.enclosures = overlap_set
self.grids = set() self.grids = set()
# Also update the grid locations with the new (possibly pruned) enclosures # Also update the grid locations with the new
# (possibly pruned) enclosures
for enclosure in self.enclosures: for enclosure in self.enclosures:
(sufficient,insufficient) = self.router.convert_pin_to_tracks(self.name,enclosure) (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
enclosure)
self.grids.update(sufficient) self.grids.update(sufficient)
@ -513,7 +530,6 @@ class pin_group:
if old_shape.overlaps(cur_shape): if old_shape.overlaps(cur_shape):
connected_set.add(cur_shape) connected_set.add(cur_shape)
# Remove the original shape # Remove the original shape
connected_set.remove(shape) connected_set.remove(shape)
@ -525,19 +541,18 @@ class pin_group:
return connected_set return connected_set
def add_enclosure(self, cell): def add_enclosure(self, cell):
""" """
Add the enclosure shape to the given cell. Add the enclosure shape to the given cell.
""" """
for enclosure in self.enclosures: for enclosure in self.enclosures:
debug.info(2,"Adding enclosure {0} {1}".format(self.name, enclosure)) debug.info(2, "Adding enclosure {0} {1}".format(self.name,
enclosure))
cell.add_rect(layer=enclosure.layer, cell.add_rect(layer=enclosure.layer,
offset=enclosure.ll(), offset=enclosure.ll(),
width=enclosure.width(), width=enclosure.width(),
height=enclosure.height()) height=enclosure.height())
def perimeter_grids(self): def perimeter_grids(self):
""" """
Return a list of the grids on the perimeter. Return a list of the grids on the perimeter.
@ -566,7 +581,6 @@ class pin_group:
return False return False
def adjacent_grids(self, other, separation): def adjacent_grids(self, other, separation):
""" """
Determine the sets of grids that are within a separation distance Determine the sets of grids that are within a separation distance
@ -594,11 +608,13 @@ 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, pin) (sufficient, insufficient)=self.router.convert_pin_to_tracks(self.name,
pin)
pin_set.update(sufficient) pin_set.update(sufficient)
partial_set.update(insufficient) partial_set.update(insufficient)
# Blockages will be a super-set of pins since it uses the inflated pin shape. # Blockages will be a super-set of pins since
# it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin) blockage_in_tracks = self.router.convert_blockage(pin)
blockage_set.update(blockage_in_tracks) blockage_set.update(blockage_in_tracks)
@ -624,12 +640,15 @@ 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, pin, expansion=1) (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name,
pin,
expansion=1)
pin_set.update(sufficient) pin_set.update(sufficient)
partial_set.update(insufficient) partial_set.update(insufficient)
if len(pin_set) == 0 and len(partial_set) == 0: if len(pin_set) == 0 and len(partial_set) == 0:
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins)) debug.error("Unable to find unblocked pin {} {}".format(self.name,
self.pins))
self.router.write_debug_gds("blocked_pin.gds") self.router.write_debug_gds("blocked_pin.gds")
# Consider all the grids that would be blocked # Consider all the grids that would be blocked

View File

@ -5,7 +5,7 @@
# (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 sys
import gdsMill import gdsMill
from tech import drc, GDS from tech import drc, GDS
from tech import layer as techlayer from tech import layer as techlayer
@ -17,10 +17,10 @@ from pin_group import pin_group
from vector import vector from vector import vector
from vector3d import vector3d from vector3d import vector3d
from globals import OPTS, print_time from globals import OPTS, print_time
from pprint import pformat
import grid_utils import grid_utils
from datetime import datetime from datetime import datetime
class router(router_tech): class router(router_tech):
""" """
A router class to read an obstruction map from a gds and plan a A router class to read an obstruction map from a gds and plan a
@ -31,7 +31,8 @@ class router(router_tech):
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
route on top of this. The blockages from the gds/module will be considered. route on top of this. The blockages from the gds/module will be
considered.
""" """
router_tech.__init__(self, layers, rail_track_width) router_tech.__init__(self, layers, rail_track_width)
@ -51,30 +52,35 @@ class router(router_tech):
self.top_name = self.layout.rootStructureName self.top_name = self.layout.rootStructureName
# print_time("GDS read",datetime.now(), start_time) # print_time("GDS read",datetime.now(), start_time)
### The pin data structures # The pin data structures
# A map of pin names to a set of pin_layout structures # A map of pin names to a set of pin_layout structures
# (i.e. pins with a given label) # (i.e. pins with a given label)
self.pins = {} self.pins = {}
# This is a set of all pins (ignoring names) so that can quickly not create blockages for pins # This is a set of all pins (ignoring names) so that can quickly
# (They will be blocked when we are routing other nets based on their name.) # not create blockages for pins
# (They will be blocked when we are routing other
# nets based on their name.)
self.all_pins = set() self.all_pins = set()
# The labeled pins above categorized into pin groups that are touching/connected. # The labeled pins above categorized into pin groups
# that are touching/connected.
self.pin_groups = {} self.pin_groups = {}
### The blockage data structures # The blockage data structures
# A list of metal shapes (using the same pin_layout structure) that are not pins but blockages. # A list of metal shapes (using the same pin_layout structure)
# that are not pins but blockages.
self.blockages = [] self.blockages = []
# The corresponding set of blocked grids for above pin shapes # The corresponding set of blocked grids for above pin shapes
self.blocked_grids = set() self.blocked_grids = set()
### The routed data structures # The routed data structures
# A list of paths that have been "routed" # A list of paths that have been "routed"
self.paths = [] self.paths = []
# A list of path blockages (they might be expanded for wide metal DRC) # A list of path blockages (they might be expanded for wide metal DRC)
self.path_blockages = [] self.path_blockages = []
# The boundary will determine the limits to the size of the routing grid # The boundary will determine the limits to the size
# of the routing grid
self.boundary = self.layout.measureBoundary(self.top_name) self.boundary = self.layout.measureBoundary(self.top_name)
# These must be un-indexed to get rid of the matrix type # These must be un-indexed to get rid of the matrix type
self.ll = vector(self.boundary[0][0], self.boundary[0][1]) self.ll = vector(self.boundary[0][0], self.boundary[0][1])
@ -91,19 +97,17 @@ class router(router_tech):
# DO NOT clear the blockages as these don't change # DO NOT clear the blockages as these don't change
self.rg.reinit() self.rg.reinit()
def set_top(self, top_name): def set_top(self, top_name):
""" If we want to route something besides the top-level cell.""" """ If we want to route something besides the top-level cell."""
self.top_name = top_name self.top_name = top_name
def is_wave(self, path): def is_wave(self, path):
""" """
Determines if this is a multi-track width wave (True) or a normal route (False) Determines if this is a multi-track width wave (True)
# or a normal route (False)
""" """
return len(path[0]) > 1 return len(path[0]) > 1
def retrieve_pins(self, pin_name): def retrieve_pins(self, pin_name):
""" """
Retrieve the pin shapes on metal 3 from the layout. Retrieve the pin shapes on metal 3 from the layout.
@ -121,7 +125,8 @@ class router(router_tech):
pin = pin_layout(pin_name, rect, layer) pin = pin_layout(pin_name, rect, layer)
pin_set.add(pin) pin_set.add(pin)
debug.check(len(pin_set)>0,"Did not find any pin shapes for {0}.".format(str(pin_name))) debug.check(len(pin_set) > 0,
"Did not find any pin shapes for {0}.".format(str(pin_name)))
self.pins[pin_name] = pin_set self.pins[pin_name] = pin_set
self.all_pins.update(pin_set) self.all_pins.update(pin_set)
@ -129,12 +134,11 @@ class router(router_tech):
for pin in self.pins[pin_name]: for pin in self.pins[pin_name]:
debug.info(3, "Retrieved pin {}".format(str(pin))) debug.info(3, "Retrieved pin {}".format(str(pin)))
def find_blockages(self): def find_blockages(self):
""" """
Iterate through all the layers and write the obstacles to the routing grid. Iterate through all the layers and write the obstacles to the routing grid.
This doesn't consider whether the obstacles will be pins or not. They get reset later This doesn't consider whether the obstacles will be pins or not.
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 layer in [self.vert_layer_number, self.horiz_layer_number]:
@ -144,8 +148,9 @@ class router(router_tech):
""" """
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 finds the pin shapes and sorts them into "groups" that
# This must come before the blockages, so we can not count the pins themselves # are connected. This must come before the blockages, so we
# can not count the pins themselves
# as blockages. # as blockages.
start_time = datetime.now() start_time = datetime.now()
for pin_name in pin_list: for pin_name in pin_list:
@ -169,7 +174,8 @@ class router(router_tech):
print_time("Converting blockages", datetime.now(), start_time, 4) print_time("Converting blockages", datetime.now(), start_time, 4)
# This will convert the pins to grid units # This will convert the pins to grid units
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids # It must be done after blockages to ensure no DRCs
# between expanded pins and blocked grids
start_time = datetime.now() start_time = datetime.now()
for pin in pin_list: for pin in pin_list:
self.convert_pins(pin) self.convert_pins(pin)
@ -184,18 +190,19 @@ class router(router_tech):
# print_time("Combining adjacent pins",datetime.now(), start_time, 4) # print_time("Combining adjacent pins",datetime.now(), start_time, 4)
# Separate any adjacent grids of differing net names that overlap # Separate any adjacent grids of differing net names
# that overlap
# Must be done before enclosing pins # Must be done before enclosing pins
start_time = datetime.now() start_time = datetime.now()
self.separate_adjacent_pins(0) self.separate_adjacent_pins(0)
print_time("Separating adjacent pins", datetime.now(), start_time, 4) print_time("Separating adjacent pins", datetime.now(), start_time, 4)
# Enclose the continguous grid units in a metal rectangle to fix some DRCs # Enclose the continguous grid units in a metal
# rectangle to fix some DRCs
start_time = datetime.now() start_time = datetime.now()
self.enclose_pins() self.enclose_pins()
print_time("Enclosing pins", datetime.now(), start_time, 4) print_time("Enclosing pins", datetime.now(), start_time, 4)
# MRG: Removing this code for now. The later compute enclosure code # MRG: Removing this code for now. The later compute enclosure code
# assumes that all pins are touching and this may produce sets of pins # assumes that all pins are touching and this may produce sets of pins
# that are not connected. # that are not connected.
@ -249,15 +256,15 @@ class router(router_tech):
# # Use the new pin group! # # Use the new pin group!
# self.pin_groups[pin_name] = new_pin_groups # self.pin_groups[pin_name] = new_pin_groups
# removed_pairs = old_size - len(new_pin_groups) # removed_pairs = old_size - len(new_pin_groups)
# debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) # debug.info(1,
# "Combined {0} pin groups for {1}".format(removed_pairs,pin_name))
# return removed_pairs # return removed_pairs
def separate_adjacent_pins(self, separation): def separate_adjacent_pins(self, separation):
""" """
This will try to separate all grid pins by the supplied number of separation This will try to separate all grid pins by the supplied
tracks (default is to prevent adjacency). number of separation tracks (default is to prevent adjacency).
""" """
# Commented out to debug with SCMOS # Commented out to debug with SCMOS
# if separation==0: # if separation==0:
@ -279,7 +286,9 @@ class router(router_tech):
If so, reduce the pin group grid to not include the adjacent grid. If so, reduce the pin group grid to not include the adjacent grid.
Try to do this intelligently to keep th pins enclosed. Try to do this intelligently to keep th pins enclosed.
""" """
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) debug.info(1,
"Comparing {0} and {1} adjacency".format(pin_name1,
pin_name2))
removed_grids = 0 removed_grids = 0
for index1, pg1 in enumerate(self.pin_groups[pin_name1]): for index1, pg1 in enumerate(self.pin_groups[pin_name1]):
for index2, pg2 in enumerate(self.pin_groups[pin_name2]): for index2, pg2 in enumerate(self.pin_groups[pin_name2]):
@ -287,7 +296,10 @@ class router(router_tech):
removed_grids += len(adj_grids) removed_grids += len(adj_grids)
# These should have the same length, so... # These should have the same length, so...
if len(adj_grids) > 0: if len(adj_grids) > 0:
debug.info(3,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids)) debug.info(3,
"Adjacent grids {0} {1} adj={2}".format(index1,
index2,
adj_grids))
self.remove_adjacent_grid(pg1, pg2, adj_grids) self.remove_adjacent_grid(pg1, pg2, adj_grids)
@ -296,7 +308,8 @@ class router(router_tech):
def remove_adjacent_grid(self, pg1, pg2, adj_grids): def remove_adjacent_grid(self, pg1, pg2, adj_grids):
""" """
Remove one of the adjacent grids in a heuristic manner. Remove one of the adjacent grids in a heuristic manner.
This will try to keep the groups similar sized by removing from the bigger group. This will try to keep the groups similar sized by
removing from the bigger group.
""" """
if pg1.size() > pg2.size(): if pg1.size() > pg2.size():
@ -309,31 +322,33 @@ class router(router_tech):
for adj in adj_grids: for adj in adj_grids:
# If the adjacent grids are a subset of the secondary grids (i.e. not necessary) # If the adjacent grids are a subset of the secondary
# remove them from each # grids (i.e. not necessary) remove them from each
if adj in bigger.secondary_grids: if adj in bigger.secondary_grids:
debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger)) debug.info(3,"Removing {} from bigger secondary {}".format(adj,
bigger))
bigger.grids.remove(adj) bigger.grids.remove(adj)
bigger.secondary_grids.remove(adj) bigger.secondary_grids.remove(adj)
self.blocked_grids.add(adj) self.blocked_grids.add(adj)
elif adj in smaller.secondary_grids: elif adj in smaller.secondary_grids:
debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller)) debug.info(3,"Removing {} from smaller secondary {}".format(adj,
smaller))
smaller.grids.remove(adj) smaller.grids.remove(adj)
smaller.secondary_grids.remove(adj) smaller.secondary_grids.remove(adj)
self.blocked_grids.add(adj) self.blocked_grids.add(adj)
else: else:
# If we couldn't remove from a secondary grid, we must remove from the primary # If we couldn't remove from a secondary grid,
# we must remove from the primary
# grid of at least one pin # grid of at least one pin
if adj in bigger.grids: if adj in bigger.grids:
debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger)) debug.info(3,"Removing {} from bigger primary {}".format(adj,
bigger))
bigger.grids.remove(adj) bigger.grids.remove(adj)
elif adj in smaller.grids: elif adj in smaller.grids:
debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller)) debug.info(3,"Removing {} from smaller primary {}".format(adj,
smaller))
smaller.grids.remove(adj) smaller.grids.remove(adj)
def prepare_blockages(self, pin_name): def prepare_blockages(self, pin_name):
""" """
Reset and add all of the blockages in the design. Reset and add all of the blockages in the design.
@ -347,10 +362,12 @@ class router(router_tech):
#print("BLOCKING:", self.blocked_grids) #print("BLOCKING:", self.blocked_grids)
self.set_blockages(self.blocked_grids, True) self.set_blockages(self.blocked_grids, True)
# Block all of the supply rails (some will be unblocked if they're a target) # Block all of the supply rails
# (some will be unblocked if they're a target)
self.set_supply_rail_blocked(True) self.set_supply_rail_blocked(True)
# Block all of the pin components (some will be unblocked if they're a source/target) # Block all of the pin components
# (some will be unblocked if they're a source/target)
# Also block the previous routes # Also block the previous routes
for name in self.pin_groups: for name in self.pin_groups:
blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} blockage_grids = {y for x in self.pin_groups[name] for y in x.grids}
@ -368,7 +385,6 @@ class router(router_tech):
blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids}
self.set_blockages(blockage_grids, False) self.set_blockages(blockage_grids, False)
def convert_shape_to_units(self, shape): def convert_shape_to_units(self, shape):
""" """
Scale a shape (two vector list) to user units Scale a shape (two vector list) to user units
@ -378,7 +394,6 @@ class router(router_tech):
ur = shape[1].scale(unit_factor) ur = shape[1].scale(unit_factor)
return [ll, ur] return [ll, ur]
def min_max_coord(self, coord): def min_max_coord(self, coord):
""" """
Find the lowest and highest corner of a Rectangle Find the lowest and highest corner of a Rectangle
@ -444,7 +459,6 @@ 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, layer_num):
""" """
Recursive find boundaries as blockages to the routing grid. Recursive find boundaries as blockages to the routing grid.
@ -455,13 +469,15 @@ class router(router_tech):
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)),rect,layer_num) new_pin = pin_layout("blockage{}".format(len(self.blockages)),
rect,
layer_num)
# If there is a rectangle that is the same in the pins, it isn't a blockage! # If there is a rectangle that is the same in the pins,
# it isn't a blockage!
if new_pin not in self.all_pins: if new_pin not in self.all_pins:
self.blockages.append(new_pin) self.blockages.append(new_pin)
def convert_point_to_units(self, p): def convert_point_to_units(self, p):
""" """
Convert a path set of tracks to center line path. Convert a path set of tracks to center line path.
@ -476,7 +492,6 @@ class router(router_tech):
""" """
return [self.convert_point_to_units(i) for i in wave] return [self.convert_point_to_units(i) for i in wave]
def convert_blockage_to_tracks(self, shape): def convert_blockage_to_tracks(self, shape):
""" """
Convert a rectangular blockage shape into track units. Convert a rectangular blockage shape into track units.
@ -487,8 +502,6 @@ class router(router_tech):
# to scale coordinates to tracks # to scale coordinates to tracks
debug.info(3, "Converting [ {0} , {1} ]".format(ll, ur)) debug.info(3, "Converting [ {0} , {1} ]".format(ll, ur))
old_ll = ll
old_ur = ur
ll = ll.scale(self.track_factor) ll = ll.scale(self.track_factor)
ur = ur.scale(self.track_factor) ur = ur.scale(self.track_factor)
# We can round since we are using inflated shapes # We can round since we are using inflated shapes
@ -500,8 +513,10 @@ class router(router_tech):
def convert_pin_to_tracks(self, pin_name, pin, expansion=0): def convert_pin_to_tracks(self, pin_name, pin, expansion=0):
""" """
Convert a rectangular pin shape into a list of track locations,layers. Convert a rectangular pin shape into a list of track locations,layers.
If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked. If no pins are "on-grid" (i.e. sufficient overlap)
If expansion>0, expamine areas beyond the current pin when it is blocked. it makes the one with most overlap if it is not blocked.
If expansion>0, expamine areas beyond the current pin
when it is blocked.
""" """
(ll, ur) = pin.rect (ll, ur) = pin.rect
debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur)) debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur))
@ -517,17 +532,22 @@ class router(router_tech):
zindex = self.get_zindex(pin.layer_num) zindex = self.get_zindex(pin.layer_num)
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, vector3d(x,y,zindex)) (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin,
vector3d(x,
y,
zindex))
if full_overlap: if full_overlap:
sufficient_list.update([full_overlap]) sufficient_list.update([full_overlap])
if partial_overlap: if partial_overlap:
insufficient_list.update([partial_overlap]) insufficient_list.update([partial_overlap])
debug.info(2,"Converting [ {0} , {1} ] full={2}".format(x,y, full_overlap)) debug.info(2,
"Converting [ {0} , {1} ] full={2}".format(x,
y,
full_overlap))
# Return all grids with any potential overlap (sufficient or not) # Return all grids with any potential overlap (sufficient or not)
return (sufficient_list, insufficient_list) return (sufficient_list, insufficient_list)
def get_all_offgrid_pin(self, pin, insufficient_list): def get_all_offgrid_pin(self, pin, insufficient_list):
""" """
Find a list of all pins with some overlap. Find a list of all pins with some overlap.
@ -598,23 +618,31 @@ class router(router_tech):
return set([best_coord]) return set([best_coord])
def convert_pin_coord_to_tracks(self, pin, coord): def convert_pin_coord_to_tracks(self, pin, coord):
""" """
Return all tracks that an inflated pin overlaps Return all tracks that an inflated pin overlaps
""" """
# This is using the full track shape rather than a single track pin shape # 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. # because we will later patch a connector if there isn't overlap.
track_pin = self.convert_track_to_shape_pin(coord) track_pin = self.convert_track_to_shape_pin(coord)
# This is the normal pin inflated by a minimum design rule # This is the normal pin inflated by a minimum design rule
inflated_pin = pin_layout(pin.name, pin.inflate(0.5*self.track_space), pin.layer) inflated_pin = pin_layout(pin.name,
pin.inflate(0.5 * self.track_space),
pin.layer)
overlap_length = pin.overlap_length(track_pin) overlap_length = pin.overlap_length(track_pin)
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord,
pin.rect,
track_pin,
overlap_length))
inflated_overlap_length = inflated_pin.overlap_length(track_pin) inflated_overlap_length = inflated_pin.overlap_length(track_pin)
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length)) debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord,
inflated_pin.rect,
track_pin,
inflated_overlap_length))
# If it overlaps with the pin, it is sufficient # If it overlaps with the pin, it is sufficient
if overlap_length == math.inf or overlap_length > 0: if overlap_length == math.inf or overlap_length > 0:
@ -628,7 +656,6 @@ class router(router_tech):
debug.info(2, " No overlap: {0} {1}".format(overlap_length, 0)) debug.info(2, " No overlap: {0} {1}".format(overlap_length, 0))
return (None, None) return (None, None)
def convert_track_to_pin(self, track): def convert_track_to_pin(self, track):
""" """
Convert a grid point into a rectangle shape that is centered Convert a grid point into a rectangle shape that is centered
@ -649,8 +676,8 @@ class router(router_tech):
def convert_track_to_shape_pin(self, track): def convert_track_to_shape_pin(self, track):
""" """
Convert a grid point into a rectangle shape that occupies the entire centered Convert a grid point into a rectangle shape
track. that occupies the entire centered track.
""" """
# to scale coordinates to tracks # to scale coordinates to tracks
x = track[0]*self.track_width - 0.5*self.track_width x = track[0]*self.track_width - 0.5*self.track_width
@ -664,8 +691,8 @@ class router(router_tech):
def convert_track_to_shape(self, track): def convert_track_to_shape(self, track):
""" """
Convert a grid point into a rectangle shape that occupies the entire centered Convert a grid point into a rectangle shape
track. that occupies the entire centered track.
""" """
# to scale coordinates to tracks # to scale coordinates to tracks
try: try:
@ -681,7 +708,8 @@ class router(router_tech):
def convert_track_to_inflated_pin(self, track): def convert_track_to_inflated_pin(self, track):
""" """
Convert a grid point into a rectangle shape that is inflated by a half DRC space. Convert a grid point into a rectangle shape
that is inflated by a half DRC space.
""" """
# calculate lower left # calculate lower left
x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space
@ -698,7 +726,8 @@ class router(router_tech):
def analyze_pins(self, pin_name): def analyze_pins(self, pin_name):
""" """
Analyze the shapes of a pin and combine them into pin_groups which are connected. Analyze the shapes of a pin and combine
them into pin_groups which are connected.
""" """
debug.info(2, "Analyzing pin groups for {}.".format(pin_name)) debug.info(2, "Analyzing pin groups for {}.".format(pin_name))
pin_set = self.pins[pin_name] pin_set = self.pins[pin_name]
@ -714,7 +743,7 @@ class router(router_tech):
# Map the pins to the lower indices # Map the pins to the lower indices
bottom_index_map = {x[1]: i for i, x in enumerate(y_coordinates) if x[2] == "bottom"} bottom_index_map = {x[1]: i for i, x in enumerate(y_coordinates) if x[2] == "bottom"}
top_index_map = {x[1]:i for i,x in enumerate(y_coordinates) if x[2]=="bottom"} # top_index_map = {x[1]: i for i, x in enumerate(y_coordinates) if x[2] == "bottom"}
# Sort the pin list by x coordinate # Sort the pin list by x coordinate
pin_list = list(pin_set) pin_list = list(pin_set)
@ -754,19 +783,19 @@ class router(router_tech):
if group_id[pin] == group_id[p2]: if group_id[pin] == group_id[p2]:
group_id[pin] = group_id[p1] group_id[pin] = group_id[p1]
# For each pin add it to it's group # For each pin add it to it's group
group_map = {} group_map = {}
for pin in pin_list: for pin in pin_list:
gid = group_id[pin] gid = group_id[pin]
if gid not in group_map: if gid not in group_map:
group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self) group_map[gid] = pin_group(name=pin_name,
pin_set=[],
router=self)
# We always add it to the first set since they are touching # We always add it to the first set since they are touching
group_map[gid].pins.add(pin) group_map[gid].pins.add(pin)
self.pin_groups[pin_name] = list(group_map.values()) self.pin_groups[pin_name] = list(group_map.values())
def convert_pins(self, pin_name): def convert_pins(self, pin_name):
""" """
Convert the pin groups into pin tracks and blockage tracks. Convert the pin groups into pin tracks and blockage tracks.
@ -775,8 +804,6 @@ class router(router_tech):
for pg in self.pin_groups[pin_name]: for pg in self.pin_groups[pin_name]:
pg.convert_pin() pg.convert_pin()
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
@ -813,10 +840,12 @@ class router(router_tech):
def add_pin_component_source(self, pin_name, index): def add_pin_component_source(self, pin_name, index):
""" """
This will mark only the pin tracks from the indexed pin component as a source. This will mark only the pin tracks
from the indexed pin component as a source.
It also unsets it as a blockage. It also unsets it as a blockage.
""" """
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.") debug.check(index<self.num_pin_components(pin_name),
"Pin component index too large.")
pin_in_tracks = self.pin_groups[pin_name][index].grids pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(2,"Set source: " + str(pin_name) + " " + str(pin_in_tracks)) debug.info(2,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
@ -832,7 +861,8 @@ class router(router_tech):
def add_pin_component_target(self, pin_name, index): def add_pin_component_target(self, pin_name, index):
""" """
This will mark only the pin tracks from the indexed pin component as a target. This will mark only the pin tracks
from the indexed pin component as a target.
It also unsets it as a blockage. It also unsets it as a blockage.
""" """
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.") debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
@ -841,7 +871,6 @@ class router(router_tech):
debug.info(2, "Set target: " + str(pin_name) + " " + str(pin_in_tracks)) debug.info(2, "Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks) self.rg.add_target(pin_in_tracks)
def add_pin_component_target_except(self, pin_name, index): def add_pin_component_target_except(self, pin_name, index):
""" """
This will mark the grids for all *other* pin components as a target. This will mark the grids for all *other* pin components as a target.
@ -859,11 +888,11 @@ class router(router_tech):
for pg in self.pin_groups[pin_name]: for pg in self.pin_groups[pin_name]:
self.set_blockages(pg.grids, value) self.set_blockages(pg.grids, value)
def prepare_path(self,path): def prepare_path(self,path):
""" """
Prepare a path or wave for routing ebedding. Prepare a path or wave for routing ebedding.
This tracks the path, simplifies the path and marks it as a path for debug output. This tracks the path, simplifies the path and
marks it as a path for debug output.
""" """
debug.info(4, "Set path: " + str(path)) debug.info(4, "Set path: " + str(path))
@ -871,8 +900,8 @@ class router(router_tech):
path.set_path() path.set_path()
# For debugging... if the path failed to route. # For debugging... if the path failed to route.
if False or path==None: # if False or path == None:
self.write_debug_gds() # self.write_debug_gds()
# First, simplify the path for # First, simplify the path for
# debug.info(1, str(self.path)) # debug.info(1, str(self.path))
@ -881,7 +910,6 @@ class router(router_tech):
return contracted_path return contracted_path
def add_route(self, path): def add_route(self, path):
""" """
Add the current wire route to the given design instance. Add the current wire route to the given design instance.
@ -908,7 +936,8 @@ class router(router_tech):
def add_single_enclosure(self, track): def add_single_enclosure(self, track):
""" """
Add a metal enclosure that is the size of the routing grid minus a spacing on each side. Add a metal enclosure that is the size of
the routing grid minus a spacing on each side.
""" """
pin = self.convert_track_to_pin(track) pin = self.convert_track_to_pin(track)
(ll, ur) = pin.rect (ll, ur) = pin.rect
@ -917,8 +946,6 @@ class router(router_tech):
width=ur.x-ll.x, width=ur.x-ll.x,
height=ur.y-ll.y) height=ur.y-ll.y)
def add_via(self, loc, size=1): def add_via(self, loc, size=1):
""" """
Add a via centered at the current location Add a via centered at the current location
@ -935,7 +962,8 @@ class router(router_tech):
""" """
layer = self.get_layer(zindex) layer = self.get_layer(zindex)
# This finds the pin shape enclosed by the track with DRC spacing on the sides # This finds the pin shape enclosed by the
# track with DRC spacing on the sides
pin = self.convert_track_to_pin(ll) pin = self.convert_track_to_pin(ll)
(abs_ll, unused) = pin.rect (abs_ll, unused) = pin.rect
pin = self.convert_track_to_pin(ur) pin = self.convert_track_to_pin(ur)
@ -967,8 +995,6 @@ class router(router_tech):
newpath.append(path[-1]) newpath.append(path[-1])
return newpath return newpath
def run_router(self, detour_scale): def run_router(self, detour_scale):
""" """
This assumes the blockages, source, and target are all set up. This assumes the blockages, source, and target are all set up.
@ -998,7 +1024,6 @@ class router(router_tech):
return False return False
return True return True
def annotate_pin_and_tracks(self, pin, tracks): def annotate_pin_and_tracks(self, pin, tracks):
"""" """"
Annotate some shapes for debug purposes Annotate some shapes for debug purposes
@ -1023,7 +1048,8 @@ class router(router_tech):
def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True): def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True):
""" """
Write out a GDS file with the routing grid and search information annotated on it. Write out a GDS file with the routing grid and
search information annotated on it.
""" """
debug.info(0, "Writing annotated router gds file to {}".format(gds_name)) debug.info(0, "Writing annotated router gds file to {}".format(gds_name))
self.del_router_info() self.del_router_info()
@ -1062,7 +1088,7 @@ class router(router_tech):
t = self.rg.map[g].get_cost() t = self.rg.map[g].get_cost()
partial_track = vector(self.track_width/6.0, 0) partial_track = vector(self.track_width/6.0, 0)
if t!=None: if t:
if g[2] == 1: if g[2] == 1:
# Upper layer is right label # Upper layer is right label
type_off = off + partial_track type_off = off + partial_track
@ -1086,7 +1112,6 @@ class router(router_tech):
layer_num = techlayer["text"] layer_num = techlayer["text"]
self.cell.objs = [x for x in self.cell.objs if x.layerNumber != layer_num] self.cell.objs = [x for x in self.cell.objs if x.layerNumber != layer_num]
def add_router_info(self): def add_router_info(self):
""" """
Write the routing grid and router cost, blockage, pins on Write the routing grid and router cost, blockage, pins on
@ -1125,14 +1150,18 @@ class router(router_tech):
if not pg.enclosed: if not pg.enclosed:
continue continue
for pin in pg.enclosures: for pin in pg.enclosures:
#print("enclosure: ",pin.name,pin.ll(),pin.width(),pin.height()) # print("enclosure: ",
# pin.name,
# pin.ll(),
# pin.width(),
# pin.height())
self.cell.add_rect(layer="text", self.cell.add_rect(layer="text",
offset=pin.ll(), offset=pin.ll(),
width=pin.width(), width=pin.width(),
height=pin.height()) height=pin.height())
# FIXME: This should be replaced with vector.snap_to_grid at some point
# FIXME: This should be replaced with vector.snap_to_grid at some point
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
@ -1141,6 +1170,7 @@ def snap_to_grid(offset):
yoff = snap_val_to_grid(offset[1]) yoff = snap_val_to_grid(offset[1])
return vector(xoff, yoff) return vector(xoff, yoff)
def snap_val_to_grid(x): def snap_val_to_grid(x):
grid = drc("grid") grid = drc("grid")
xgrid = int(round(round((x / grid), 2), 0)) xgrid = int(round(round((x / grid), 2), 0))