Refactor router to have pin_groups for pins and router_tech file

This commit is contained in:
Matt Guthaus 2018-10-25 13:36:35 -07:00
parent 3f17679000
commit 0544d02ca2
4 changed files with 378 additions and 357 deletions

View File

@ -0,0 +1,238 @@
from vector3d import vector3d
from tech import drc
import debug
class pin_group:
"""
A class to represent a group of touching rectangular design pin.
It requires a router to define the track widths and blockages which
determine how pin shapes get mapped to tracks.
"""
def __init__(self, name, pin_shapes, router):
self.name = name
# Flag for when it is routed
self.routed = False
self.shapes = pin_shapes
self.router = router
# These are the corresponding pin grids for each pin group.
self.grids = set()
# The corresponding set of partially blocked grids for each pin group.
# These are blockages for other nets but unblocked for routing this group.
self.blockages = set()
def set_routed(self, value=True):
self.routed = value
def is_routed(self):
return self.routed
def remove_redundant_shapes(self, pin_list):
"""
Remove any pin layout that is contained within another.
"""
local_debug = False
if local_debug:
debug.info(0,"INITIAL:",pin_list)
# Make a copy of the list to start
new_pin_list = pin_list.copy()
# This is n^2, but the number is small
for pin1 in pin_list:
for pin2 in pin_list:
# Can't contain yourself
if pin1 == pin2:
continue
if pin2.contains(pin1):
# It may have already been removed by being enclosed in another pin
if pin1 in new_pin_list:
new_pin_list.remove(pin1)
if local_debug:
debug.info(0,"FINAL :",new_pin_list)
return new_pin_list
# FIXME: This relies on some technology parameters from router which is not clearn.
def compute_enclosures(self):
"""
Find the minimum rectangle enclosures of the given tracks.
"""
# Enumerate every possible enclosure
pin_list = []
for seed in self.grids:
(ll, ur) = self.enclose_pin_grids(seed)
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
pin_list.append(enclosure)
#return pin_list
# We used to do this, but smaller enclosures can be
return self.remove_redundant_shapes(pin_list)
def compute_enclosure(self, pin, enclosure):
"""
Compute an enclosure to connect the pin to the enclosure shape.
This assumes the shape will be the dimension of the pin.
"""
if pin.xoverlaps(enclosure):
# Is it vertical overlap, extend pin shape to enclosure
plc = pin.lc()
prc = pin.rc()
elc = enclosure.lc()
erc = enclosure.rc()
ymin = min(plc.y,elc.y)
ymax = max(plc.y,elc.y)
ll = vector(plc.x, ymin)
ur = vector(prc.x, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
elif pin.yoverlaps(enclosure):
# Is it horizontal overlap, extend pin shape to enclosure
pbc = pin.bc()
puc = pin.uc()
ebc = enclosure.bc()
euc = enclosure.uc()
xmin = min(pbc.x,ebc.x)
xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y)
ur = vector(xmax, puc.y)
p = pin_layout(pin.name, [ll, ur], pin.layer)
else:
# Neither, so we must do a corner-to corner
pc = pin.center()
ec = enclosure.center()
xmin = min(pc.x, ec.x)
xmax = max(pc.x, ec.x)
ymin = min(pc.y, ec.y)
ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin)
ur = vector(xmax, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
return p
def find_smallest_connector(self, enclosure_list):
"""
Compute all of the connectors between non-overlapping pins and enclosures.
Return the smallest.
"""
smallest = None
for pin in self.shapes:
for enclosure in enclosure_list:
new_enclosure = self.compute_enclosure(pin, enclosure)
if smallest == None or new_enclosure.area()<smallest.area():
smallest = new_enclosure
return smallest
def find_smallest_overlapping(self, shape_list):
"""
Find the smallest area shape in shape_list that overlaps with any
pin in pin_list by a min width.
"""
smallest_shape = None
for pin in self.shapes:
# They may not be all on the same layer... in the future.
zindex=self.router.get_zindex(pin.layer_num)
(min_width,min_space) = self.router.get_layer_width_space(zindex)
# Now compare it with every other shape to check how much they overlap
for other in shape_list:
overlap_length = pin.overlap_length(other)
if overlap_length > min_width:
if smallest_shape == None or other.area()<smallest_shape.area():
smallest_shape = other
return smallest_shape
def overlap_any_shape(self, pin_list, shape_list):
"""
Does the given pin overlap any of the shapes in the pin list.
"""
for pin in pin_list:
for other in shape_list:
if pin.overlaps(other):
return True
return False
def max_pin_layout(self, pin_list):
"""
Return the max area pin_layout
"""
biggest = pin_list[0]
for pin in pin_list:
if pin.area() > biggest.area():
biggest = pin
return pin
def enclose_pin_grids(self, seed):
"""
This encloses a single pin component with a rectangle
starting with the seed and expanding right until blocked
and then up until blocked.
"""
# We may have started with an empty set
if not self.grids:
return None
# Start with the seed
ll = seed
# Start with the ll and make the widest row
row = [ll]
# Move right while we can
while True:
right = row[-1] + vector3d(1,0,0)
# Can't move if not in the pin shape
if right in self.grids and right not in self.router.blocked_grids:
row.append(right)
else:
break
# Move up while we can
while True:
next_row = [x+vector3d(0,1,0) for x in row]
for cell in next_row:
# Can't move if any cell is not in the pin shape
if cell not in self.grids or cell in self.router.blocked_grids:
break
else:
row = next_row
# Skips the second break
continue
# Breaks from the nested break
break
# Add a shape from ll to ur
ur = row[-1]
return (ll,ur)
def enclose_pin(self):
"""
This will find the biggest rectangle enclosing some grid squares and
put a rectangle over it. It does not enclose grid squares that are blocked
by other shapes.
"""
# Compute the enclosure pin_layout list of the set of tracks
enclosure_list = self.compute_enclosures()
self.enclosure = self.find_smallest_overlapping(enclosure_list)
if not self.enclosure:
self.enclosure = self.find_smallest_connector(enclosure_list)
debug.info(2,"Computed enclosure {0} {1}".format(self.name, self.enclosure))
def add_enclosure(self, cell):
"""
Add the enclosure shape to the given cell.
"""
debug.info(2,"Adding enclosure {0} {1}".format(self.name, self.enclosure))
self.router.cell.add_rect(layer=self.enclosure.layer,
offset=self.enclosure.ll(),
width=self.enclosure.width(),
height=self.enclosure.height())

View File

@ -1,17 +1,18 @@
import sys import sys
import gdsMill import gdsMill
from tech import drc,GDS,layer from tech import drc,GDS
from contact import contact
import math import math
import debug import debug
from router_tech import router_tech
from pin_layout import pin_layout from pin_layout import pin_layout
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 from globals import OPTS
from pprint import pformat from pprint import pformat
import grid_utils import grid_utils
class router: 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
route on a given layer. This is limited to two layer routes. route on a given layer. This is limited to two layer routes.
@ -23,6 +24,8 @@ class router:
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)
self.cell = design self.cell = design
# If didn't specify a gds blockage file, write it out to read the gds # If didn't specify a gds blockage file, write it out to read the gds
@ -37,22 +40,16 @@ class router:
self.reader.loadFromFile(gds_filename) self.reader.loadFromFile(gds_filename)
self.top_name = self.layout.rootStructureName self.top_name = self.layout.rootStructureName
# Set up layers and track sizes
self.set_layers(layers)
### The pin data structures ### The pin data structures
# A map of pin names to pin structures # A map of pin names to a set of pin_layout structures
self.pins = {} self.pins = {}
# This is a set of all pins so that we don't create blockages for these shapes. # This is a set of all pins (ignoring names) so that can quickly not create blockages for pins
# (They will be blocked based on the names we are routing)
self.all_pins = set() self.all_pins = set()
# This is a set of pin groups. Each group consists of overlapping pin shapes on the same layer. # A map of pin names to a list of pin groups
# A pin group is a set overlapping pin shapes on the same layer.
self.pin_groups = {} self.pin_groups = {}
# These are the corresponding pin grids for each pin group.
self.pin_grids = {}
# The corresponding set of partially blocked grids for each pin group.
# These are blockages for other nets but unblocked for this component.
self.pin_blockages = {}
### 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.
@ -78,8 +75,6 @@ class router:
self.pins = {} self.pins = {}
self.all_pins = set() self.all_pins = set()
self.pin_groups = {} self.pin_groups = {}
self.pin_grids = {}
self.pin_blockages = {}
# 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()
@ -88,19 +83,6 @@ class router:
""" 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 get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number:
return 0
else:
return 1
def get_layer(self, zindex):
if zindex==1:
return self.vert_layer_name
elif zindex==0:
return self.horiz_layer_name
else:
debug.error("Invalid zindex {}".format(zindex),-1)
def is_wave(self,path): def is_wave(self,path):
""" """
@ -108,40 +90,6 @@ class router:
""" """
return len(path[0])>1 return len(path[0])>1
def set_layers(self, layers):
"""
Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always
vertical.
"""
self.layers = layers
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
# This is the minimum routed track spacing
via_connect = contact(self.layers, (1, 1))
self.max_via_size = max(via_connect.width,via_connect.height)
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
self.vert_layer_number = layer[self.vert_layer_name]
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
self.horiz_layer_number = layer[self.horiz_layer_name]
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
self.vert_track_width = self.max_via_size + self.vert_layer_spacing
# We'll keep horizontal and vertical tracks the same for simplicity.
self.track_width = max(self.horiz_track_width,self.vert_track_width)
debug.info(1,"Track width: "+str(self.track_width))
self.track_widths = [self.track_width] * 2
self.track_factor = [1/self.track_width] * 2
debug.info(1,"Track factor: {0}".format(self.track_factor))
# 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_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
def retrieve_pins(self,pin_name): def retrieve_pins(self,pin_name):
""" """
@ -224,14 +172,16 @@ class router:
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)
for name in self.pin_grids.keys(): for name in self.pin_groups.keys():
self.set_blockages(self.pin_grids[name],True) blockage_grids = {y for x in self.pin_groups[name] for y in x.grids}
self.set_blockages(blockage_grids,True)
# Don't mark the other components as targets since we want to route # Don't mark the other components as targets since we want to route
# directly to a rail, but unblock all the source components so we can # directly to a rail, but unblock all the source components so we can
# route over them # route over them
self.set_blockages(self.pin_grids[pin_name],False) blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids}
self.set_blockages(blockage_grids,False)
# These are the paths that have already been routed. # These are the paths that have already been routed.
self.set_path_blockages() self.set_path_blockages()
@ -458,23 +408,6 @@ class router:
return set([best_coord]) return set([best_coord])
def get_layer_width_space(self, zindex, width=0, length=0):
"""
Return the width and spacing of a given layer
and wire of a given width and length.
"""
if zindex==1:
layer_name = self.vert_layer_name
elif zindex==0:
layer_name = self.horiz_layer_name
else:
debug.error("Invalid zindex for track", -1)
min_width = drc("minwidth_{0}".format(layer_name), width, length)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length)
return (min_width,min_spacing)
def convert_pin_coord_to_tracks(self, pin, coord): def convert_pin_coord_to_tracks(self, pin, coord):
""" """
Given a pin and a track coordinate, determine if the pin overlaps enough. Given a pin and a track coordinate, determine if the pin overlaps enough.
@ -599,24 +532,18 @@ class router:
reduced_classes = combine_classes(equiv_classes) reduced_classes = combine_classes(equiv_classes)
if local_debug: if local_debug:
debug.info(0,"FINAL ",reduced_classes) debug.info(0,"FINAL ",reduced_classes)
self.pin_groups[pin_name]=reduced_classes self.pin_groups[pin_name] = [pin_group(name=pin_name, pin_shapes=x, router=self) for x in reduced_classes]
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.
""" """
try:
self.pin_grids[pin_name]
except:
self.pin_grids[pin_name] = []
found_pin = False
for pg in self.pin_groups[pin_name]: for pg in self.pin_groups[pin_name]:
#print("PG ",pg) #print("PG ",pg)
# Keep the same groups for each pin # Keep the same groups for each pin
pin_set = set() pin_set = set()
blockage_set = set() blockage_set = set()
for pin in pg: for pin in pg.shapes:
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.convert_pin_to_tracks(pin_name, pin) pin_in_tracks=self.convert_pin_to_tracks(pin_name, pin)
@ -644,7 +571,7 @@ class router:
debug.error("Unable to find unblocked pin on grid.") debug.error("Unable to find unblocked pin on grid.")
# We need to route each of the components, so don't combine the groups # We need to route each of the components, so don't combine the groups
self.pin_grids[pin_name].append(pin_set | blockage_set) pg.grids = pin_set | blockage_set
# Add all of the partial blocked grids to the set for the design # Add all of the partial blocked grids to the set for the design
# if they are not blocked by other metal # if they are not blocked by other metal
@ -658,143 +585,6 @@ class router:
#self.blocked_grids.difference_update(pin_set) #self.blocked_grids.difference_update(pin_set)
def enclose_pin_grids(self, grids, seed):
"""
This encloses a single pin component with a rectangle
starting with the seed and expanding right until blocked
and then up until blocked.
"""
# We may have started with an empty set
if not grids:
return None
# Start with the seed
ll = seed
# Start with the ll and make the widest row
row = [ll]
# Move right while we can
while True:
right = row[-1] + vector3d(1,0,0)
# Can't move if not in the pin shape
if right in grids and right not in self.blocked_grids:
row.append(right)
else:
break
# Move up while we can
while True:
next_row = [x+vector3d(0,1,0) for x in row]
for cell in next_row:
# Can't move if any cell is not in the pin shape
if cell not in grids or cell in self.blocked_grids:
break
else:
row = next_row
# Skips the second break
continue
# Breaks from the nested break
break
# Add a shape from ll to ur
ur = row[-1]
return self.compute_pin_enclosure(ll, ur, ll.z)
def remove_redundant_shapes(self, pin_list):
"""
Remove any pin layout that is contained within another.
"""
local_debug = False
if local_debug:
debug.info(0,"INITIAL:",pin_list)
# Make a copy of the list to start
new_pin_list = pin_list.copy()
# This is n^2, but the number is small
for pin1 in pin_list:
for pin2 in pin_list:
# Can't contain yourself
if pin1 == pin2:
continue
if pin2.contains(pin1):
# It may have already been removed by being enclosed in another pin
if pin1 in new_pin_list:
new_pin_list.remove(pin1)
if local_debug:
debug.info(0,"FINAL :",new_pin_list)
return new_pin_list
def compute_enclosures(self, tracks):
"""
Find the minimum rectangle enclosures of the given tracks.
"""
# Enumerate every possible enclosure
pin_list = []
for seed in tracks:
pin_list.append(self.enclose_pin_grids(tracks, seed))
#return pin_list
# We used to do this, but smaller enclosures can be
return self.remove_redundant_shapes(pin_list)
def overlap_any_shape(self, pin_list, shape_list):
"""
Does the given pin overlap any of the shapes in the pin list.
"""
for pin in pin_list:
for other in shape_list:
if pin.overlaps(other):
return True
return False
def find_smallest_overlapping(self, pin_list, shape_list):
"""
Find the smallest area shape in shape_list that overlaps with any
pin in pin_list by a min width.
"""
smallest_shape = None
for pin in pin_list:
# They may not be all on the same layer... in the future.
zindex=self.get_zindex(pin.layer_num)
(min_width,min_space) = self.get_layer_width_space(zindex)
# Now compare it with every other shape to check how much they overlap
for other in shape_list:
overlap_length = pin.overlap_length(other)
if overlap_length > min_width:
if smallest_shape == None or other.area()<smallest_shape.area():
smallest_shape = other
return smallest_shape
def max_pin_layout(self, pin_list):
"""
Return the max area pin_layout
"""
biggest = pin_list[0]
for pin in pin_list:
if pin.area() > biggest.area():
biggest = pin
return pin
def find_smallest_connector(self, pin_list, enclosure_list):
"""
Compute all of the connectors between non-overlapping pins and enclosures.
Return the smallest.
"""
smallest = None
for pin in pin_list:
for enclosure in enclosure_list:
new_enclosure = self.compute_enclosure(pin, enclosure)
if smallest == None or new_enclosure.area()<smallest.area():
smallest = new_enclosure
return smallest
def enclose_pins(self): def enclose_pins(self):
""" """
@ -806,86 +596,20 @@ class router:
self.connector_enclosure = [] self.connector_enclosure = []
self.enclosures = [] self.enclosures = []
for pin_name in self.pin_grids.keys(): for pin_name in self.pin_groups.keys():
debug.info(1,"Enclosing pins for {}".format(pin_name)) debug.info(1,"Enclosing pins for {}".format(pin_name))
debug.check(len(self.pin_groups[pin_name])==len(self.pin_grids[pin_name]),"Unequal pin_group and pin_grid") for pg in self.pin_groups[pin_name]:
for pin_group,pin_grid_set in zip(self.pin_groups[pin_name],self.pin_grids[pin_name]): pg.enclose_pin()
pg.add_enclosure(self.cell)
# Compute the enclosure pin_layout list of the set of tracks
enclosure_list = self.compute_enclosures(pin_grid_set)
smallest_enclosure = self.find_smallest_overlapping(pin_group, enclosure_list)
if smallest_enclosure:
#for smallest_enclosure in enclosure_list:
debug.info(2,"Adding enclosure {0} {1}".format(pin_name, smallest_enclosure))
self.cell.add_rect(layer=smallest_enclosure.layer,
offset=smallest_enclosure.ll(),
width=smallest_enclosure.width(),
height=smallest_enclosure.height())
self.enclosures.append(smallest_enclosure)
#if self.overlap_any_shape(pin_group, enclosure_list):
#debug.info(2,"Pin overlaps enclosure {0}".format(pin_name))
# pass
else:
new_enclosure = self.find_smallest_connector(pin_group, enclosure_list)
debug.info(2,"Adding connector enclosure {0} {1}".format(pin_name, new_enclosure))
self.cell.add_rect(layer=new_enclosure.layer,
offset=new_enclosure.ll(),
width=new_enclosure.width(),
height=new_enclosure.height())
self.connector_enclosure.append(new_enclosure)
#self.write_debug_gds("pin_debug.gds", True) #self.write_debug_gds("pin_debug.gds", True)
def compute_enclosure(self, pin, enclosure):
"""
Compute an enclosure to connect the pin to the enclosure shape.
This assumes the shape will be the dimension of the pin.
"""
if pin.xoverlaps(enclosure):
# Is it vertical overlap, extend pin shape to enclosure
plc = pin.lc()
prc = pin.rc()
elc = enclosure.lc()
erc = enclosure.rc()
ymin = min(plc.y,elc.y)
ymax = max(plc.y,elc.y)
ll = vector(plc.x, ymin)
ur = vector(prc.x, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
elif pin.yoverlaps(enclosure):
# Is it horizontal overlap, extend pin shape to enclosure
pbc = pin.bc()
puc = pin.uc()
ebc = enclosure.bc()
euc = enclosure.uc()
xmin = min(pbc.x,ebc.x)
xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y)
ur = vector(xmax, puc.y)
p = pin_layout(pin.name, [ll, ur], pin.layer)
else:
# Neither, so we must do a corner-to corner
pc = pin.center()
ec = enclosure.center()
xmin = min(pc.x, ec.x)
xmax = max(pc.x, ec.x)
ymin = min(pc.y, ec.y)
ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin)
ur = vector(xmax, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
return p
def add_source(self, pin_name): def add_source(self, pin_name):
""" """
This will mark the grids for all pin components as a source. This will mark the grids for all pin components as a source.
Marking as source or target also clears blockage status. Marking as source or target also clears blockage status.
""" """
for i in range(self.num_pin_grids(pin_name)): for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_source(pin_name, i) self.add_pin_component_source(pin_name, i)
def add_target(self, pin_name): def add_target(self, pin_name):
@ -893,14 +617,14 @@ class router:
This will mark the grids for all pin components as a target. This will mark the grids for all pin components as a target.
Marking as source or target also clears blockage status. Marking as source or target also clears blockage status.
""" """
for i in range(self.num_pin_grids(pin_name)): for i in range(self.num_pin_components(pin_name)):
self.add_pin_component_target(pin_name, i) self.add_pin_component_target(pin_name, i)
def num_pin_components(self, pin_name): def num_pin_components(self, pin_name):
""" """
This returns how many disconnected pin components there are. This returns how many disconnected pin components there are.
""" """
return len(self.pin_grids[pin_name]) return len(self.pin_groups[pin_name])
def add_pin_component_source(self, pin_name, index): def add_pin_component_source(self, pin_name, index):
""" """
@ -909,7 +633,7 @@ class router:
""" """
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_grids[pin_name][index] pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks)) debug.info(1,"Set source: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_source(pin_in_tracks) self.rg.add_source(pin_in_tracks)
@ -928,7 +652,7 @@ class router:
""" """
debug.check(index<self.num_pin_grids(pin_name),"Pin component index too large.") debug.check(index<self.num_pin_grids(pin_name),"Pin component index too large.")
pin_in_tracks = self.pin_grids[pin_name][index] pin_in_tracks = self.pin_groups[pin_name][index].grids
debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks) self.rg.add_target(pin_in_tracks)
@ -938,8 +662,8 @@ class router:
Block all of the pin components. Block all of the pin components.
""" """
debug.info(2,"Setting blockages {0} {1}".format(pin_name,value)) debug.info(2,"Setting blockages {0} {1}".format(pin_name,value))
for component in self.pin_grids[pin_name]: for pg in self.pin_groups[pin_name]:
self.set_blockages(component, value) self.set_blockages(pg.grids, value)
def prepare_path(self,path): def prepare_path(self,path):
@ -1011,8 +735,7 @@ class router:
def compute_pin_enclosure(self, ll, ur, zindex, name=""): def compute_pin_enclosure(self, ll, ur, zindex, name=""):
""" """
Enclose the tracks from ll to ur in a single rectangle that meets Enclose the tracks from ll to ur in a single rectangle that meets
the track DRC rules. If name is supplied, it is added as a pin and the track DRC rules.
not just a rectangle.
""" """
# Get the layer information # Get the layer information
(width, space) = self.get_layer_width_space(zindex) (width, space) = self.get_layer_width_space(zindex)
@ -1034,8 +757,6 @@ class router:
def compute_wide_enclosure(self, ll, ur, zindex, name=""): def compute_wide_enclosure(self, ll, ur, zindex, name=""):
""" """
Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules. Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules.
If name is supplied, it is added as a pin and not just a rectangle.
""" """
# Find the pin enclosure of the whole track shape (ignoring DRCs) # Find the pin enclosure of the whole track shape (ignoring DRCs)
@ -1064,19 +785,6 @@ class router:
return pin return pin
def get_inertia(self,p0,p1):
"""
Sets the direction based on the previous direction we came from.
"""
# direction (index) of movement
if p0.x!=p1.x:
return 0
elif p0.y!=p1.y:
return 1
else:
# z direction
return 2
def contract_path(self,path): def contract_path(self,path):
""" """
Remove intermediate points in a rectilinear path or a wave. Remove intermediate points in a rectilinear path or a wave.

View File

@ -0,0 +1,78 @@
from tech import drc,layer
from contact import contact
from pin_group import pin_group
import debug
class router_tech:
"""
This is a class to hold the router tech constants.
"""
def __init__(self, layers):
"""
Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always
vertical.
"""
self.layers = layers
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
# This is the minimum routed track spacing
via_connect = contact(self.layers, (1, 1))
self.max_via_size = max(via_connect.width,via_connect.height)
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
self.vert_layer_number = layer[self.vert_layer_name]
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
self.horiz_layer_number = layer[self.horiz_layer_name]
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
self.vert_track_width = self.max_via_size + self.vert_layer_spacing
# We'll keep horizontal and vertical tracks the same for simplicity.
self.track_width = max(self.horiz_track_width,self.vert_track_width)
debug.info(1,"Track width: "+str(self.track_width))
self.track_widths = [self.track_width] * 2
self.track_factor = [1/self.track_width] * 2
debug.info(1,"Track factor: {0}".format(self.track_factor))
# 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_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
def get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number:
return 0
else:
return 1
def get_layer(self, zindex):
if zindex==1:
return self.vert_layer_name
elif zindex==0:
return self.horiz_layer_name
else:
debug.error("Invalid zindex {}".format(zindex),-1)
def get_layer_width_space(self, zindex, width=0, length=0):
"""
Return the width and spacing of a given layer
and wire of a given width and length.
"""
if zindex==1:
layer_name = self.vert_layer_name
elif zindex==0:
layer_name = self.horiz_layer_name
else:
debug.error("Invalid zindex for track", -1)
min_width = drc("minwidth_{0}".format(layer_name), width, length)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), width, length)
return (min_width,min_spacing)

View File

@ -1,9 +1,10 @@
import gdsMill import gdsMill
import tech import tech
from contact import contact
import math import math
import debug import debug
from globals import OPTS from globals import OPTS
from contact import contact
from pin_group import pin_group
from pin_layout import pin_layout from pin_layout import pin_layout
from vector3d import vector3d from vector3d import vector3d
from router import router from router import router
@ -83,15 +84,15 @@ class supply_router(router):
# Determine the rail locations # Determine the rail locations
self.route_supply_rails(self.vdd_name,1) self.route_supply_rails(self.vdd_name,1)
#self.write_debug_gds("debug_rails.gds",stop_program=True) #self.write_debug_gds("debug_rails.gds",stop_program=True)
remaining_vdd_pin_indices = self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(vdd_name)
remaining_gnd_pin_indices = self.route_simple_overlaps(gnd_name) self.route_simple_overlaps(gnd_name)
#self.write_debug_gds("debug_simple_route.gds",stop_program=True) #self.write_debug_gds("debug_simple_route.gds",stop_program=True)
# Route the supply pins to the supply rails # Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter # Route vdd first since we want it to be shorter
self.route_pins_to_rails(vdd_name, remaining_vdd_pin_indices) self.route_pins_to_rails(vdd_name)
self.route_pins_to_rails(gnd_name, remaining_gnd_pin_indices) self.route_pins_to_rails(gnd_name)
#self.write_debug_gds("debug_pin_routes.gds",stop_program=True) #self.write_debug_gds("debug_pin_routes.gds",stop_program=True)
#self.write_debug_gds("final.gds") #self.write_debug_gds("final.gds")
@ -105,21 +106,18 @@ class supply_router(router):
This checks for simple cases where a pin component already overlaps a supply rail. This checks for simple cases where a pin component already overlaps a supply rail.
It will add an enclosure to ensure the overlap in wide DRC rule cases. It will add an enclosure to ensure the overlap in wide DRC rule cases.
""" """
num_components = self.num_pin_components(pin_name)
remaining_pins = []
supply_tracks = self.supply_rail_tracks[pin_name] supply_tracks = self.supply_rail_tracks[pin_name]
for pg in self.pin_groups[pin_name]:
for index in range(num_components): if pg.is_routed():
pin_in_tracks = self.pin_grids[pin_name][index] continue
common_set = supply_tracks & pin_in_tracks
if len(common_set)==0:
# if no overlap, add it to the complex route pins
remaining_pins.append(index)
else:
self.create_simple_overlap_enclosure(pin_name, common_set)
return remaining_pins common_set = supply_tracks & pg.grids
if len(common_set)>0:
self.create_simple_overlap_enclosure(pin_name, common_set)
pg.set_routed()
def recurse_simple_overlap_enclosure(self, pin_name, start_set, direct): def recurse_simple_overlap_enclosure(self, pin_name, start_set, direct):
""" """
@ -167,7 +165,9 @@ class supply_router(router):
if not new_set: if not new_set:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.WEST) new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.WEST)
enclosure_list = self.compute_enclosures(new_set) pg = pin_group(name=pin_name, pin_shapes=[], router=self)
pg.grids=new_set
enclosure_list = pg.compute_enclosures()
for pin in enclosure_list: for pin in enclosure_list:
debug.info(2,"Adding simple overlap enclosure {0} {1}".format(pin_name, pin)) debug.info(2,"Adding simple overlap enclosure {0} {1}".format(pin_name, pin))
self.cell.add_rect(layer=pin.layer, self.cell.add_rect(layer=pin.layer,
@ -464,23 +464,26 @@ class supply_router(router):
self.supply_rail_wire_tracks[pin_name] = wire_set self.supply_rail_wire_tracks[pin_name] = wire_set
def route_pins_to_rails(self, pin_name, remaining_component_indices): def route_pins_to_rails(self, pin_name):
""" """
This will route each of the remaining pin components to the supply rails. This will route each of the remaining pin components to the supply rails.
After it is done, the cells are added to the pin blockage list. After it is done, the cells are added to the pin blockage list.
""" """
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
debug.info(1,"Pin {0} has {1} remaining components to route.".format(pin_name, debug.info(1,"Pin {0} has {1} remaining components to route.".format(pin_name,
len(remaining_component_indices))) remaining_components))
recent_paths = [] for index,pg in enumerate(self.pin_groups[pin_name]):
# For every component if pg.is_routed():
for index in remaining_component_indices: continue
debug.info(2,"Routing component {0} {1}".format(pin_name, index)) debug.info(2,"Routing component {0} {1}".format(pin_name, index))
# Clear everything in the routing grid.
self.rg.reinit() self.rg.reinit()
# This is inefficient since it is non-incremental, but it was
# easier to debug.
self.prepare_blockages(pin_name) self.prepare_blockages(pin_name)
# Add the single component of the pin as the source # Add the single component of the pin as the source
@ -491,16 +494,10 @@ class supply_router(router):
# Don't add the other pins, but we could? # Don't add the other pins, but we could?
self.add_supply_rail_target(pin_name) self.add_supply_rail_target(pin_name)
# Add the previous paths as targets too
#self.add_path_target(recent_paths)
#print(self.rg.target)
# Actually run the A* router # Actually run the A* router
if not self.run_router(detour_scale=5): if not self.run_router(detour_scale=5):
self.write_debug_gds() self.write_debug_gds()
recent_paths.append(self.paths[-1])
def add_supply_rail_target(self, pin_name): def add_supply_rail_target(self, pin_name):