Fix instersection bug. Improve primary and secondary pin algo.

This commit is contained in:
Matt Guthaus 2018-12-04 16:53:04 -08:00
parent 7ce75398a8
commit 126d4a8d10
3 changed files with 102 additions and 103 deletions

View File

@ -441,10 +441,10 @@ class pin_layout:
""" """
Given three co-linear points, determine if q lies on segment pr Given three co-linear points, determine if q lies on segment pr
""" """
if q[0] <= max(p[0], r[0]) and \ if q.x <= max(p.x, r.x) and \
q[0] >= min(p[0], r[0]) and \ q.x >= min(p.x, r.x) and \
q[1] <= max(p[1], r[1]) and \ q.y <= max(p.y, r.y) and \
q[1] >= min(p[1], r[1]): q.y >= min(p.y, r.y):
return True return True
return False return False
@ -473,8 +473,8 @@ class pin_layout:
x = (b2*c1 - b1*c2)/determinant x = (b2*c1 - b1*c2)/determinant
y = (a1*c2 - a2*c1)/determinant y = (a1*c2 - a2*c1)/determinant
r = [x,y] r = vector(x,y).snap_to_grid()
if self.on_segment(a, r, b) and self.on_segment(c, r, d): if self.on_segment(a, r, b) and self.on_segment(c, r, d):
return [x, y] return r
return None return None

View File

@ -561,15 +561,13 @@ class pin_group:
of any grid in the other set. of any grid in the other set.
""" """
# We could optimize this to just check the boundaries # We could optimize this to just check the boundaries
g1_grids = set() adj_grids = set()
g2_grids = set()
for g1 in self.grids: for g1 in self.grids:
for g2 in other.grids: for g2 in other.grids:
if g1.distance(g2) <= separation: if g1.distance(g2) <= separation:
g1_grids.add(g1) adj_grids.add(g1)
g2_grids.add(g2)
return g1_grids,g2_grids return adj_grids
def convert_pin(self): def convert_pin(self):
""" """
@ -578,14 +576,16 @@ class pin_group:
should be either blocked or part of the pin. should be either blocked or part of the pin.
""" """
pin_set = set() pin_set = set()
partial_set = set()
blockage_set = set() blockage_set = set()
for pin_list in self.pins: for pin_list in self.pins:
for pin in pin_list: for pin in pin_list:
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
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin) (sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(pin_in_tracks) pin_set.update(sufficient)
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)
@ -597,30 +597,35 @@ class pin_group:
if len(shared_set)>0: if len(shared_set)>0:
debug.info(2,"Removing pins {}".format(shared_set)) debug.info(2,"Removing pins {}".format(shared_set))
pin_set.difference_update(shared_set) pin_set.difference_update(shared_set)
shared_set = partial_set & self.router.blocked_grids
if len(shared_set)>0:
debug.info(2,"Removing pins {}".format(shared_set))
partial_set.difference_update(shared_set)
shared_set = blockage_set & self.router.blocked_grids shared_set = blockage_set & self.router.blocked_grids
if len(shared_set)>0: if len(shared_set)>0:
debug.info(2,"Removing blocks {}".format(shared_set)) debug.info(2,"Removing blocks {}".format(shared_set))
blockage_set.difference_update(shared_set) blockage_set.difference_update(shared_set)
# At least one of the groups must have some valid tracks # At least one of the groups must have some valid tracks
if (len(pin_set)==0 and len(blockage_set)==0): 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)) #debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
for pin_list in self.pins: for pin_list in self.pins:
for pin in pin_list: for pin in pin_list:
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
pin_in_tracks=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(pin_in_tracks) pin_set.update(sufficient)
partial_set.update(insufficient)
if len(pin_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")
# We need to route each of the components, so don't combine the groups # Consider all the grids that would be blocked
self.grids = pin_set self.grids = pin_set | partial_set
# Remember the secondary grids for removing adjacent pins in wide metal spacing # Remember the secondary grids for removing adjacent pins
self.secondary_grids = blockage_set - pin_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

@ -279,48 +279,55 @@ class router(router_tech):
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))
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]):
# FIgXME: Use separation distance and edge grids only adj_grids = pg1.adjacent_grids(pg2, separation)
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
# These should have the same length, so... # These should have the same length, so...
if len(grids_g1)>0: if len(adj_grids)>0:
debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2)) debug.info(2,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids))
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2) self.remove_adjacent_grid(pg1, pg2, adj_grids)
def remove_adjacent_grid(self, pg1, grids1, pg2, grids2): 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.
""" """
# Determine the bigger and smaller group
if pg1.size()>pg2.size(): if pg1.size()>pg2.size():
bigger = pg1 bigger = pg1
bigger_grids = grids1
smaller = pg2 smaller = pg2
smaller_grids = grids2
else: else:
bigger = pg2 bigger = pg2
bigger_grids = grids2
smaller = pg1 smaller = pg1
smaller_grids = grids1
# First, see if we can remove grids that are in the secondary grids for adj in adj_grids:
# i.e. they aren't necessary to the pin grids
if bigger_grids.issubset(bigger.secondary_grids):
debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
bigger.grids.difference_update(bigger_grids)
self.blocked_grids.update(bigger_grids)
return
elif smaller_grids.issubset(smaller.secondary_grids):
debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
smaller.grids.difference_update(smaller_grids)
self.blocked_grids.update(smaller_grids)
return
# If that fails, just randomly remove from the bigger one and give a warning.
# This might fail later. # If the adjacent grids are a subset of the secondary grids (i.e. not necessary)
debug.info(1,"Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids)) # remove them from each
debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids)) removed_flag = False
bigger.grids.difference_update(bigger_grids) if adj in bigger.secondary_grids:
self.blocked_grids.update(bigger_grids) debug.info(2,"Removing {} from bigger secondary {}".format(adj, bigger))
bigger.grids.remove(adj)
bigger.secondary_grids.remove(adj)
self.blocked_grids.add(adj)
removed_flag=True
if adj in smaller.secondary_grids:
debug.info(2,"Removing {} from smaller secondary {}".format(adj, smaller))
smaller.gris.remove(adj)
secondary.secondary_grids.remove(adj)
self.blocked_grids.add(adj)
removed_flag=True
# If we couldn't remove from a secondary grid, we must remove from the primary
# grid of at least one pin
if not removed_flag:
if adj in bigger.grids:
debug.info(2,"Removing {} from bigger primary {}".format(adj, bigger))
bigger.grids.remove(adj)
elif adj in smaller.grids:
debug.info(2,"Removing {} from smaller primary {}".format(adj, smaller))
smaller.grids.remove(adj)
@ -523,31 +530,16 @@ 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} partial={3}".format(x,y, full_overlap, partial_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)
# # Remove the blocked grids
# sufficient_list.difference_update(self.blocked_grids)
# insufficient_list.difference_update(self.blocked_grids)
# if len(sufficient_list)>0:
# return sufficient_list
# elif expansion==0 and len(insufficient_list)>0:
# best_pin = self.get_all_offgrid_pin(pin, insufficient_list)
# #print(best_pin)
# return best_pin
# elif expansion>0:
# nearest_pin = self.get_furthest_offgrid_pin(pin, insufficient_list)
# return nearest_pin
# else:
# return set()
def get_all_offgrid_pin(self, pin, insufficient_list): def get_all_offgrid_pin(self, pin, insufficient_list):
""" """
@ -623,47 +615,34 @@ class router(router_tech):
return set([best_coord]) return set([best_coord])
def convert_pin_coord_to_minimal_tracks(self, pin, coord):
"""
Given a pin and a track coordinate, determine if the pin overlaps enough.
If it does, add additional metal to make the pin "on grid".
If it doesn't, add it to the blocked grid list.
"""
(width, spacing) = self.get_layer_width_space(coord.z)
# This is the rectangle if we put a pin in the center of the track
track_pin = self.convert_track_to_pin(coord)
overlap_length = pin.overlap_length(track_pin)
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
# If it overlaps by more than the min width DRC, we can just use the track
if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width):
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing))
return (coord, None)
# Otherwise, keep track of the partial overlap grids in case we need to patch it later.
else:
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing))
return (None, 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 the rectangle if we put a pin in the center of the track # This is using the full track shape rather than a single track pin shape
track_pin = self.convert_track_to_inflated_pin(coord) # because we will later patch a connector if there isn't overlap.
overlap_length = pin.overlap_length(track_pin) track_pin = self.convert_track_to_shape_pin(coord)
# 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)
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) overlap_length = pin.overlap_length(track_pin)
# If it overlaps by more than the min width DRC, we can just use the track debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
if overlap_length==math.inf or snap_val_to_grid(overlap_length) > 0: inflated_overlap_length = inflated_pin.overlap_length(track_pin)
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,0)) debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length))
return (coord, None)
# Otherwise, keep track of the partial overlap grids in case we need to patch it later. # If it overlaps with the pin, it is sufficient
if overlap_length==math.inf or overlap_length > 0:
debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0))
return (coord,None)
# If it overlaps with the inflated pin, it is partial
elif inflated_overlap_length==math.inf or inflated_overlap_length > 0:
debug.info(2," Partial overlap: {0} >? {1}".format(inflated_overlap_length,0))
return (None,coord)
else: else:
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,0)) debug.info(2," No overlap: {0} {1}".format(overlap_length,0))
return (None, coord) return (None,None)
@ -688,6 +667,21 @@ class router(router_tech):
p = pin_layout("", [ll, ur], self.get_layer(track[2])) p = pin_layout("", [ll, ur], self.get_layer(track[2]))
return p return p
def convert_track_to_shape_pin(self, track):
"""
Convert a grid point into a rectangle shape that occupies the entire centered
track.
"""
# to scale coordinates to tracks
x = track[0]*self.track_width - 0.5*self.track_width
y = track[1]*self.track_width - 0.5*self.track_width
# offset lowest corner object to to (-track halo,-track halo)
ll = snap_to_grid(vector(x,y))
ur = snap_to_grid(ll + vector(self.track_width,self.track_width))
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
return p
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 that occupies the entire centered
@ -701,7 +695,7 @@ class router(router_tech):
ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) ur = snap_to_grid(ll + vector(self.track_width,self.track_width))
return [ll,ur] return [ll,ur]
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.