Component shape functions. Find connected pins through overlaps.

This commit is contained in:
Matt Guthaus 2018-09-08 10:05:48 -07:00
parent 69261a0dc1
commit 96c51f3464
5 changed files with 272 additions and 149 deletions

View File

@ -64,22 +64,16 @@ class grid:
self.add_map(n)
self.map[n].path=value
def add_blockage_shape(self,ll,ur,z):
debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
block_list = []
for x in range(int(ll[0]),int(ur[0])+1):
for y in range(int(ll[1]),int(ur[1])+1):
block_list.append(vector3d(x,y,z))
self.add_blockage(block_list)
def add_blockage(self,block_list):
def set_blockages(self,block_list,value=True):
debug.info(2,"Adding blockage list={0}".format(str(block_list)))
for n in block_list:
self.set_blocked(n)
self.set_blocked(n,value)
def clear_blockages(self):
debug.info(2,"Clearing all blockages")
for n in self.map.keys():
self.set_blocked(n,False)
def set_source(self,n):
if isinstance(n,list):
for item in n:

View File

@ -7,6 +7,7 @@ from pin_layout import pin_layout
from vector import vector
from vector3d import vector3d
from globals import OPTS
from pprint import pformat
class router:
"""
@ -37,10 +38,14 @@ class router:
# A map of pin names to pin structures
self.pins = {}
# A map of pin names to pin structures
self.pins_grids = {}
# A list of pin blockages (represented by the pin structures too)
# A set of connected pin groups
self.pin_groups = {}
# The corresponding sets of grids of the groups
self.pin_grids = {}
# The set of partially covered pins to avoid
self.pin_blockages = {}
# A list of blockages
self.blockages=[]
# all the paths we've routed so far (to supplement the blockages)
@ -59,6 +64,9 @@ class router:
Keep the other blockages unchanged.
"""
self.pins = {}
self.pin_groups = {}
self.pin_grids = {}
self.pin_blockages = {}
self.rg.reinit()
def set_top(self,top_name):
@ -120,12 +128,10 @@ class router:
def find_pin(self,pin_name):
"""
Finds the pin shapes and converts to tracks.
Pin can either be a label or a location,layer pair: [[x,y],layer].
def retrieve_pins(self,pin_name):
"""
Retrieve the pin shapes from the layout.
"""
shape_list=self.layout.getAllPinShapesByLabel(str(pin_name))
pin_list = []
for shape in shape_list:
@ -136,8 +142,16 @@ class router:
pin_list.append(pin)
debug.check(len(pin_list)>0,"Did not find any pin shapes for {0}.".format(str(pin)))
self.pins[pin_name] = pin_list
return pin_list
def find_pins(self,pin_name):
"""
Finds the pin shapes and converts to tracks.
Pin can either be a label or a location,layer pair: [[x,y],layer].
"""
self.retrieve_pins(pin_name)
self.analyze_pins(pin_name)
self.convert_pins(pin_name)
def find_blockages(self):
@ -147,7 +161,9 @@ class router:
if they are not actually a blockage.
"""
for layer in [self.vert_layer_number,self.horiz_layer_number]:
self.get_blockages(layer)
self.retrieve_blockages(layer)
self.convert_blockages()
def clear_pins(self):
"""
@ -157,6 +173,9 @@ class router:
Keep the other blockages unchanged.
"""
self.pins = {}
self.pin_groups = {}
self.pin_grids = {}
self.pin_blockages = {}
# DO NOT clear the blockages as these don't change
self.rg.reinit()
@ -218,36 +237,51 @@ class router:
for path in self.paths:
for grid in path:
self.rg.set_blocked(grid)
def clear_blockages(self):
"""
Clear all blockages on the grid.
"""
self.rg.clear_blockages()
def add_blockages(self):
def add_pin_blockages(self, pin_name):
""" Add the blockages except the pin shapes. Also remove the pin shapes from the blockages list. """
# Join all the pin shapes into one big list
all_pins = [item for sublist in list(self.pins.values()) for item in sublist]
self.add_blockages(self.pin_blockages[pin_name])
def add_blockages(self, blockages=None):
""" Flag the blockages in the grid """
if blockages == None:
blockages = self.blockage_grids
self.rg.set_blockages(blockages)
def get_blockage_tracks(self, ll, ur, z):
debug.info(3,"Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
# Do an n^2 check to see if any shapes are the same, otherwise add them
# FIXME: Make faster, but number of pins won't be *that* large
real_blockages = []
block_list = []
for x in range(int(ll[0]),int(ur[0])+1):
for y in range(int(ll[1]),int(ur[1])+1):
block_list.append(vector3d(x,y,z))
return block_list
def convert_blockages(self):
""" Convert blockages to grid tracks. """
blockage_grids = []
for blockage in self.blockages:
for pin in all_pins:
# If the blockage overlaps the pin and is on the same layer,
# it must be connected, so skip it.
if blockage.overlaps(pin):
debug.info(1,"Removing blockage for pin {}".format(str(pin)))
break
else:
debug.info(2,"Adding blockage {}".format(str(blockage)))
# Inflate the blockage by spacing rule
[ll,ur]=self.convert_blockage_to_tracks(blockage.inflate())
zlayer = self.get_zindex(blockage.layer_num)
self.rg.add_blockage_shape(ll,ur,zlayer)
real_blockages.append(blockage)
debug.info(2,"Converting blockage {}".format(str(blockage)))
# Inflate the blockage by spacing rule
[ll,ur]=self.convert_blockage_to_tracks(blockage.inflate())
zlayer = self.get_zindex(blockage.layer_num)
blockages = self.get_blockage_tracks(ll,ur,zlayer)
blockage_grids.extend(blockages)
# Remember the filtered blockages
self.blockages = real_blockages
self.blockage_grids = blockage_grids
def get_blockages(self, layer_num):
def retrieve_blockages(self, layer_num):
"""
Recursive find boundaries as blockages to the routing grid.
"""
@ -261,7 +295,7 @@ class router:
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.
"""
@ -269,14 +303,14 @@ class router:
pt = pt.scale(self.track_widths[0],self.track_widths[1],1)
return pt
def convert_wave_to_units(self,wave):
def convert_wave_to_units(self, wave):
"""
Convert a wave to a set of center points
"""
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.
"""
@ -353,7 +387,7 @@ class router:
#debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur))
return (track_list,block_list)
def compute_overlap(self,r1,r2):
def compute_overlap(self, r1, r2):
""" Calculate the rectangular overlap of two rectangles. """
(r1_ll,r1_ur) = r1
(r2_ll,r2_ur) = r2
@ -370,7 +404,7 @@ class router:
return [0,0]
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
track in the track and leaves half a DRC space in each direction.
@ -393,7 +427,7 @@ class router:
return [ll,ur]
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
track.
@ -407,35 +441,131 @@ class router:
return [ll,ur]
def get_pin(self,pin_name):
def analyze_pins(self, pin_name):
"""
Gets the pin shapes only. Doesn't add to grid.
Analyze the shapes of a pin and combine them into groups which are connected.
"""
self.pins[pin_name] = self.find_pin(pin_name)
pin_list = self.pins[pin_name]
# Put each pin in an equivalence class of it's own
equiv_classes = [[x] for x in pin_list]
#print("INITIAL\n",equiv_classes)
def add_pin(self,pin_name,is_source=False):
def compare_classes(class1, class2):
"""
Determine if two classes should be combined and if so return
the combined set. Otherwise, return None.
"""
#print("CL1:\n",class1)
#print("CL2:\n",class2)
# Compare each pin in each class,
# and if any overlap, return the combined the class
for p1 in class1:
for p2 in class2:
if p1.overlaps(p2):
combined_class = class1+class2
#print("COM:",pformat(combined_class))
return combined_class
return None
def combine_classes(equiv_classes):
""" Recursive function to combine classes. """
#print("\nRECURSE:\n",pformat(equiv_classes))
if len(equiv_classes)==1:
return(equiv_classes)
for class1 in equiv_classes:
for class2 in equiv_classes:
if class1 == class2:
continue
class3 = compare_classes(class1, class2)
if class3:
new_classes = equiv_classes
new_classes.remove(class1)
new_classes.remove(class2)
new_classes.append(class3)
return(combine_classes(new_classes))
else:
return(equiv_classes)
reduced_classes = combine_classes(equiv_classes)
#print("FINAL ",reduced_classes)
self.pin_groups[pin_name] = reduced_classes
def convert_pins(self, pin_name):
"""
Mark the grids that are in the pin rectangle ranges to have the pin property.
pin can be a location or a label.
Convert the pin groups into pin tracks and blockage tracks
"""
self.pin_grids[pin_name] = []
self.pin_blockages[pin_name] = []
found_pin = False
for pin in self.pins[pin_name]:
(pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(pin)
if (len(pin_in_tracks)>0):
found_pin=True
if is_source:
debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_source(pin_in_tracks)
else:
debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks)
self.rg.add_blockage(blockage_in_tracks)
for pg in self.pin_groups[pin_name]:
for pin in pg:
(pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(pin)
# At least one of the groups must have some valid tracks
if (len(pin_in_tracks)>0):
found_pin = True
# We need to route each of the classes, so don't combine the groups
self.pin_grids[pin_name].append(pin_in_tracks)
# However, we can just block all of the partials, so combine the groups
self.pin_blockages[pin_name].extend(blockage_in_tracks)
if not found_pin:
self.write_debug_gds()
debug.check(found_pin,"Unable to find pin on grid.")
debug.error("Unable to find pin on grid.",-1)
def add_pin(self, pin_name, is_source=False):
"""
This will mark the grids for all pin components as a source or a target.
Marking as source or target also clears blockage status.
"""
for i in range(self.num_pin_components(pin_name)):
self.add_pin_component(pin_name, i, is_source)
def num_pin_components(self, pin_name):
"""
This returns how many disconnected pin components there are.
"""
return len(self.pin_grids[pin_name])
def add_pin_component(self, pin_name, index, is_source=False):
"""
This will mark only the pin tracks from the indexed pin component as a source/target.
It also unsets it as a blockage.
"""
debug.check(index<self.num_pin_components(pin_name),"Pin component index too large.")
pin_in_tracks = self.pin_grids[pin_name][index]
if is_source:
debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_source(pin_in_tracks)
else:
debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks)
def add_supply_rail_target(self, pin_name):
"""
Add the supply rails of given name as a routing target.
"""
for i in range(self.num_rails):
if self.paths[i].name == pin_name:
rail = self.paths[i]
for wave_index in range(len(rail)):
pin_in_tracks = rail[wave_index]
#debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks)
def add_component_blockages(self, pin_name):
"""
Block all of the pin components.
"""
for comp_index in range(self.num_pin_components(pin_name)):
pin_in_tracks = self.pin_grids[pin_name][comp_index]
self.add_blockages(pin_in_tracks)
def write_debug_gds(self):
"""

View File

@ -62,11 +62,14 @@ class signal_router(router):
self.find_blockages()
# Now add the blockages (all shapes except the pins)
self.get_pin(src)
self.get_pin(dest)
self.find_pins(src)
self.find_pins(dest)
# Now add the blockages
self.add_blockages()
self.add_pin_blockages(src)
self.add_pin_blockages(dest)
# Add blockages from previous paths
self.add_path_blockages()

View File

@ -15,9 +15,6 @@ class supply_grid(grid):
""" Create a routing map of width x height cells and 2 in the z-axis. """
grid.__init__(self, ll, ur, track_width)
# Current rail
self.rail = []
def reinit(self):
""" Reinitialize everything for a new route. """

View File

@ -24,6 +24,10 @@ class supply_router(router):
"""
router.__init__(self, gds_name, module)
# Power rail width in grid units.
self.rail_track_width = 2
def create_routing_grid(self):
"""
@ -41,8 +45,8 @@ class supply_router(router):
"""
debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
self.cell = cell
self.pins[vdd_name] = []
self.pins[gnd_name] = []
self.vdd_name = vdd_name
self.gnd_name = gnd_name
# Clear the pins if we have previously routed
if (hasattr(self,'rg')):
@ -58,40 +62,25 @@ class supply_router(router):
self.find_blockages()
# Get the pin shapes
self.get_pin(vdd_name)
self.get_pin(gnd_name)
self.find_pins(vdd_name)
self.find_pins(gnd_name)
# Now add the blockages (all shapes except the pins)
self.add_blockages()
self.route_supply_rails()
self.connect_supply_rails()
#self.route_pins_to_rails()
self.add_blockages()
self.add_pin_blockages(vdd_name)
self.route_supply_rails(gnd_name,0)
self.connect_supply_rail(gnd_name)
self.route_pins_to_rails(gnd_name)
self.clear_blockages()
self.add_pin_blockages(gnd_name)
self.route_supply_rails(vdd_name,1)
self.connect_supply_rail(vdd_name)
self.route_pins_to_rails(vdd_name)
# source pin will be a specific layout pin
# target pin will be the rails only
# returns the path in tracks
# (path,cost) = self.rg.route(detour_scale)
# if path:
# debug.info(1,"Found path: cost={0} ".format(cost))
# debug.info(2,str(path))
# self.add_route(path)
# return True
# else:
# self.write_debug_gds()
# # clean up so we can try a reroute
# self.clear_pins()
self.write_debug_gds()
return False
def connect_supply_rails(self):
"""
Add vias between overlapping supply rails.
"""
self.connect_supply_rail("vdd")
self.connect_supply_rail("gnd")
def connect_supply_rail(self, name):
"""
@ -117,54 +106,43 @@ class supply_router(router):
def route_supply_rails(self):
def route_supply_rails(self, name, supply_number):
"""
Add supply rails for vdd and gnd alternating in both layers.
Connect cross-over points with vias.
Route the horizontal and vertical supply rails across the entire design.
"""
# Width in grid units.
self.rail_track_width = 2
# Keep a list of all the rail wavepaths
self.paths = []
# vdd will be the even grids every 2 widths
for offset in range(0, self.rg.ur.y, 2*self.rail_track_width):
start_offset = supply_number*self.rail_track_width
max_yoffset = self.rg.ur.y
max_xoffset = self.rg.ur.x
step_offset = 2*self.rail_track_width
# Horizontal supply rails
for offset in range(start_offset, max_yoffset, step_offset):
# Seed the function at the location with the given width
wave = [vector3d(0,offset+i,0) for i in range(self.rail_track_width)]
# While we can keep expanding east
while wave and wave[0].x < self.rg.ur.x:
wave = self.route_supply_rail("vdd", wave, direction.EAST)
# While we can keep expanding east in this horizontal track
while wave and wave[0].x < max_xoffset:
wave = self.route_supply_rail(name, wave, direction.EAST)
# gnd will be the even grids every 2 widths
for offset in range(self.rail_track_width, self.rg.ur.y, 2*self.rail_track_width):
# Seed the function at the location with the given width
wave = [vector3d(0,offset+i,0) for i in range(self.rail_track_width)]
# While we can keep expanding east
while wave and wave[0].x < self.rg.ur.x:
wave = self.route_supply_rail("gnd", wave, direction.EAST)
# vdd will be the even grids every 2 widths
for offset in range(0, self.rg.ur.x, 2*self.rail_track_width):
# Vertical supply rails
max_offset = self.rg.ur.x
for offset in range(start_offset, max_xoffset, step_offset):
# Seed the function at the location with the given width
wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
# While we can keep expanding east
while wave and wave[0].y < self.rg.ur.y:
wave = self.route_supply_rail("vdd", wave, direction.NORTH)
# gnd will be the even grids every 2 widths
for offset in range(self.rail_track_width, self.rg.ur.x, 2*self.rail_track_width):
# Seed the function at the location with the given width
wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
# While we can keep expanding east
while wave and wave[0].y < self.rg.ur.y:
wave = self.route_supply_rail("gnd", wave, direction.NORTH)
# While we can keep expanding north in this vertical track
while wave and wave[0].y < max_yoffset:
wave = self.route_supply_rail(name, wave, direction.NORTH)
# Remember index of path size which is how many rails we had at the start
self.num_rails = len(self.paths)
def route_supply_rail(self, name, seed_wave, direct):
"""
Add supply rails alternating layers.
Return the final wavefront for seeding the next wave.
This finds the first valid starting location and routes a supply rail
in the given direction.
It returns the space after the end of the rail to seed another call for multiple
supply rails in the same "track" when there is a blockage.
"""
# Sweep to find an initial unblocked valid wave
@ -192,14 +170,19 @@ class supply_router(router):
def route_pins_to_rails(self):
def route_pins_to_rails(self,pin_name):
"""
This will route all the supply pins to supply rails one at a time.
After each one, it adds the cells to the blockage list.
This will route each of the pin components to the supply rails.
After it is done, the cells are added to the pin blockage list.
"""
for pin_name in self.pins.keys():
for pin in self.pins[pin_name]:
route_supply_pin(pin)
for index in range(self.num_pin_components(pin_name)):
# Block all pin components first
self.add_component_blockages(pin_name)
# Add the single component of the pin as the source (unmarks it as a blockage too)
self.add_pin_component(pin_name,index,is_source=True)
# Add all of the rails as targets
self.add_supply_rail_target(pin_name)
#route_supply_pin(pin)
@ -209,6 +192,22 @@ class supply_router(router):
Do not allow other pins to be destinations so that everything is connected
to the rails.
"""
# source pin will be a specific layout pin
# target pin will be the rails only
# returns the path in tracks
# (path,cost) = self.rg.route(detour_scale)
# if path:
# debug.info(1,"Found path: cost={0} ".format(cost))
# debug.info(2,str(path))
# self.add_route(path)
# return True
# else:
# self.write_debug_gds()
# # clean up so we can try a reroute
# self.clear_pins()
pass