Simplifying supply router to single grid track

This commit is contained in:
Matt Guthaus 2018-12-04 08:41:57 -08:00
parent 2a68b57215
commit 389bb91af4
6 changed files with 187 additions and 172 deletions

View File

@ -13,8 +13,8 @@ class options(optparse.Values):
# This is the name of the technology. # This is the name of the technology.
tech_name = "" tech_name = ""
# This is the temp directory where all intermediate results are stored. # This is the temp directory where all intermediate results are stored.
#openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid()) openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid())
openram_temp = "{0}/openram_temp/".format(os.getenv("HOME")) #openram_temp = "{0}/openram_temp/".format(os.getenv("HOME"))
# This is the verbosity level to control debug information. 0 is none, 1 # This is the verbosity level to control debug information. 0 is none, 1
# is minimal, etc. # is minimal, etc.
debug_level = 0 debug_level = 0

View File

@ -172,7 +172,6 @@ class pin_group:
ymax = max(plc.y,elc.y) ymax = max(plc.y,elc.y)
ll = vector(plc.x, ymin) ll = vector(plc.x, ymin)
ur = vector(prc.x, ymax) ur = vector(prc.x, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
elif pin.yoverlaps(enclosure): elif pin.yoverlaps(enclosure):
# Is it horizontal overlap, extend pin shape to enclosure # Is it horizontal overlap, extend pin shape to enclosure
pbc = pin.bc() pbc = pin.bc()
@ -183,7 +182,6 @@ class pin_group:
xmax = max(pbc.x,ebc.x) xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y) ll = vector(xmin, pbc.y)
ur = vector(xmax, puc.y) ur = vector(xmax, puc.y)
p = pin_layout(pin.name, [ll, ur], pin.layer)
else: else:
# Neither, so we must do a corner-to corner # Neither, so we must do a corner-to corner
pc = pin.center() pc = pin.center()
@ -194,8 +192,10 @@ class pin_group:
ymax = max(pc.y, ec.y) ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin) ll = vector(xmin, ymin)
ur = vector(xmax, ymax) ur = vector(xmax, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
if ll.x==ur.x or ll.y==ur.y:
return None
p = pin_layout(pin.name, [ll, ur], pin.layer)
return p return p
def find_above_connector(self, pin, enclosures): def find_above_connector(self, pin, enclosures):
@ -226,7 +226,7 @@ class pin_group:
# If it already overlaps, no connector needed # If it already overlaps, no connector needed
if above_item.overlaps(pin): if above_item.overlaps(pin):
return None return None
# Otherwise, make a connector to the item # Otherwise, make a connector to the item
p = self.compute_connector(pin, above_item) p = self.compute_connector(pin, above_item)
return p return p
@ -485,7 +485,9 @@ class pin_group:
for pin_list in self.pins: for pin_list in self.pins:
if not self.overlap_any_shape(pin_list, self.enclosures): if not self.overlap_any_shape(pin_list, self.enclosures):
connector = self.find_smallest_connector(pin_list, self.enclosures) connector = self.find_smallest_connector(pin_list, self.enclosures)
debug.check(connector!=None, "Could not find a connector for {} with {}".format(pin_list, self.enclosures)) if connector==None:
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
self.router.write_debug_gds("no_connector.gds")
self.enclosures.append(connector) self.enclosures.append(connector)
@ -623,61 +625,61 @@ class pin_group:
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))
def recurse_simple_overlap_enclosure(self, start_set, direct): # def recurse_simple_overlap_enclosure(self, start_set, direct):
""" # """
Recursive function to return set of tracks that connects to # Recursive function to return set of tracks that connects to
the actual supply rail wire in a given direction (or terminating # the actual supply rail wire in a given direction (or terminating
when any track is no longer in the supply rail. # when any track is no longer in the supply rail.
""" # """
next_set = grid_utils.expand_border(start_set, direct) # next_set = grid_utils.expand_border(start_set, direct)
supply_tracks = self.router.supply_rail_tracks[self.name] # supply_tracks = self.router.supply_rail_tracks[self.name]
supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name] # supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
supply_overlap = next_set & supply_tracks # supply_overlap = next_set & supply_tracks
wire_overlap = next_set & supply_wire_tracks # wire_overlap = next_set & supply_wire_tracks
# If the rail overlap is the same, we are done, since we connected to the actual wire # # If the rail overlap is the same, we are done, since we connected to the actual wire
if len(wire_overlap)==len(start_set): # if len(wire_overlap)==len(start_set):
new_set = start_set | wire_overlap # new_set = start_set | wire_overlap
# If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region # # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
elif len(supply_overlap)==len(start_set): # elif len(supply_overlap)==len(start_set):
recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct) # recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
new_set = start_set | supply_overlap | recurse_set # new_set = start_set | supply_overlap | recurse_set
else: # else:
# If we got no next set, we are done, can't expand! # # If we got no next set, we are done, can't expand!
new_set = set() # new_set = set()
return new_set # return new_set
def create_simple_overlap_enclosure(self, start_set): # def create_simple_overlap_enclosure(self, start_set):
""" # """
This takes a set of tracks that overlap a supply rail and creates an enclosure # This takes a set of tracks that overlap a supply rail and creates an enclosure
that is ensured to overlap the supply rail wire. # that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure. # It then adds rectangle(s) for the enclosure.
""" # """
additional_set = set() # additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it # # Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set)) # e = next(iter(start_set))
new_set = start_set.copy() # new_set = start_set.copy()
if e.z==0: # if e.z==0:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH) # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
if not new_set: # if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH) # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
else: # else:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST) # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
if not new_set: # if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST) # new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
# Expand the pin grid set to include some extra grids that connect the supply rail # # Expand the pin grid set to include some extra grids that connect the supply rail
self.grids.update(new_set) # self.grids.update(new_set)
# Add the inflated set so we don't get wide metal spacing issues (if it exists) # # Block the grids
self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width)) # self.blockages.update(new_set)
# Add the polygon enclosures and set this pin group as routed # # Add the polygon enclosures and set this pin group as routed
self.set_routed() # self.set_routed()
self.enclosures = self.compute_enclosures() # self.enclosures = self.compute_enclosures()

View File

@ -21,12 +21,12 @@ class router(router_tech):
It populates blockages on a grid class. It populates blockages on a grid class.
""" """
def __init__(self, layers, design, gds_filename=None): 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) router_tech.__init__(self, layers, rail_track_width)
self.cell = design self.cell = design
@ -177,15 +177,15 @@ class router(router_tech):
#print_time("Convert pins",datetime.now(), start_time) #print_time("Convert pins",datetime.now(), start_time)
#start_time = datetime.now() #start_time = datetime.now()
for pin in pin_list: #for pin in pin_list:
self.combine_adjacent_pins(pin) # self.combine_adjacent_pins(pin)
#print_time("Combine pins",datetime.now(), start_time) #print_time("Combine pins",datetime.now(), start_time)
#self.write_debug_gds("debug_combine_pins.gds",stop_program=True) #self.write_debug_gds("debug_combine_pins.gds",stop_program=True)
# Separate any adjacent grids of differing net names to prevent wide metal DRC violations # Separate any adjacent grids of differing net names to prevent wide metal DRC violations
# Must be done before enclosing pins # Must be done before enclosing pins
#start_time = datetime.now() #start_time = datetime.now()
self.separate_adjacent_pins(self.supply_rail_space_width) #self.separate_adjacent_pins(self.supply_rail_space_width)
#print_time("Separate pins",datetime.now(), start_time) #print_time("Separate pins",datetime.now(), start_time)
# For debug # For debug
#self.separate_adjacent_pins(1) #self.separate_adjacent_pins(1)
@ -201,7 +201,7 @@ class router(router_tech):
""" """
Find pins that have adjacent routing tracks and merge them into a Find pins that have adjacent routing tracks and merge them into a
single pin_group. The pins themselves may not be touching, but single pin_group. The pins themselves may not be touching, but
enclose_pis in the next step will ensure they are touching. enclose_pins in the next step will ensure they are touching.
""" """
debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
# Find all adjacencies # Find all adjacencies
@ -519,7 +519,7 @@ class router(router_tech):
# Keep tabs on tracks with sufficient and insufficient overlap # Keep tabs on tracks with sufficient and insufficient overlap
sufficient_list = set() sufficient_list = set()
insufficient_list = set() insufficient_list = set()
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):
@ -528,7 +528,7 @@ class router(router_tech):
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(4,"Converting [ {0} , {1} ] full={2} partial={3}".format(x,y, full_overlap, partial_overlap)) debug.info(3,"Converting [ {0} , {1} ] full={2} partial={3}".format(x,y, full_overlap, partial_overlap))
# Remove the blocked grids # Remove the blocked grids
sufficient_list.difference_update(self.blocked_grids) sufficient_list.difference_update(self.blocked_grids)
@ -620,7 +620,7 @@ 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_minimal_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.
If it does, add additional metal to make the pin "on grid". If it does, add additional metal to make the pin "on grid".
@ -642,6 +642,25 @@ class router(router_tech):
else: else:
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing)) debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing))
return (None, coord) return (None, coord)
def convert_pin_coord_to_tracks(self, pin, coord):
"""
Return all tracks that an inflated pin overlaps
"""
# This is the rectangle if we put a pin in the center of the track
track_pin = self.convert_track_to_inflated_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) > 0:
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,0))
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,0))
return (None, coord)
@ -653,20 +672,14 @@ class router(router_tech):
Convert a grid point into a rectangle shape that is centered Convert a grid point into a rectangle shape that is centered
track in the track and leaves half a DRC space in each direction. track in the track and leaves half a DRC space in each direction.
""" """
# space depends on which layer it is
if self.get_layer(track[2])==self.horiz_layer_name:
space = 0.5*self.horiz_layer_spacing
else:
space = 0.5*self.vert_layer_spacing
# calculate lower left # calculate lower left
x = track.x*self.track_width - 0.5*self.track_width + space x = track.x*self.track_width - 0.5*self.track_width + 0.5*self.track_space
y = track.y*self.track_width - 0.5*self.track_width + space y = track.y*self.track_width - 0.5*self.track_width + 0.5*self.track_space
ll = snap_to_grid(vector(x,y)) ll = snap_to_grid(vector(x,y))
# calculate upper right # calculate upper right
x = track.x*self.track_width + 0.5*self.track_width - space x = track.x*self.track_width + 0.5*self.track_width - 0.5*self.track_space
y = track.y*self.track_width + 0.5*self.track_width - space y = track.y*self.track_width + 0.5*self.track_width - 0.5*self.track_space
ur = snap_to_grid(vector(x,y)) ur = snap_to_grid(vector(x,y))
p = pin_layout("", [ll, ur], self.get_layer(track[2])) p = pin_layout("", [ll, ur], self.get_layer(track[2]))
@ -685,6 +698,23 @@ 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):
"""
Convert a grid point into a rectangle shape that is inflated by a half DRC space.
"""
# calculate lower left
x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space
y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space
ll = snap_to_grid(vector(x,y))
# calculate upper right
x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space
y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space
ur = snap_to_grid(vector(x,y))
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
return p
def analyze_pins(self, pin_name): def analyze_pins(self, pin_name):
""" """
@ -877,8 +907,6 @@ class router(router_tech):
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. the track DRC rules.
""" """
# Get the layer information
(width, space) = self.get_layer_width_space(zindex)
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
@ -894,35 +922,35 @@ class router(router_tech):
return pin return pin
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.
""" # """
# Find the pin enclosure of the whole track shape (ignoring DRCs) # # Find the pin enclosure of the whole track shape (ignoring DRCs)
(abs_ll,unused) = self.convert_track_to_shape(ll) # (abs_ll,unused) = self.convert_track_to_shape(ll)
(unused,abs_ur) = self.convert_track_to_shape(ur) # (unused,abs_ur) = self.convert_track_to_shape(ur)
# Get the layer information # # Get the layer information
x_distance = abs(abs_ll.x-abs_ur.x) # x_distance = abs(abs_ll.x-abs_ur.x)
y_distance = abs(abs_ll.y-abs_ur.y) # y_distance = abs(abs_ll.y-abs_ur.y)
shape_width = min(x_distance, y_distance) # shape_width = min(x_distance, y_distance)
shape_length = max(x_distance, y_distance) # shape_length = max(x_distance, y_distance)
# Get the DRC rule for the grid dimensions # # Get the DRC rule for the grid dimensions
(width, space) = self.get_layer_width_space(zindex, shape_width, shape_length) # (width, space) = self.get_supply_layer_width_space(zindex)
layer = self.get_layer(zindex) # layer = self.get_layer(zindex)
if zindex==0: # if zindex==0:
spacing = vector(0.5*self.track_width, 0.5*space) # spacing = vector(0.5*self.track_width, 0.5*space)
else: # else:
spacing = vector(0.5*space, 0.5*self.track_width) # spacing = vector(0.5*space, 0.5*self.track_width)
# Compute the shape offsets with correct spacing # # Compute the shape offsets with correct spacing
new_ll = abs_ll + spacing # new_ll = abs_ll + spacing
new_ur = abs_ur - spacing # new_ur = abs_ur - spacing
pin = pin_layout(name, [new_ll, new_ur], layer) # pin = pin_layout(name, [new_ll, new_ur], layer)
return pin # return pin
def contract_path(self,path): def contract_path(self,path):
@ -963,8 +991,7 @@ class router(router_tech):
self.add_route(path) self.add_route(path)
path_set = grid_utils.flatten_set(path) path_set = grid_utils.flatten_set(path)
inflated_path = grid_utils.inflate_set(path_set,self.supply_rail_space_width) self.path_blockages.append(path_set)
self.path_blockages.append(inflated_path)
else: else:
self.write_debug_gds("failed_route.gds") self.write_debug_gds("failed_route.gds")
# clean up so we can try a reroute # clean up so we can try a reroute

View File

@ -9,13 +9,15 @@ class router_tech:
""" """
This is a class to hold the router tech constants. This is a class to hold the router tech constants.
""" """
def __init__(self, layers, supply_router=False): def __init__(self, layers, rail_track_width):
""" """
Allows us to change the layers that we are routing on. First layer Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always is always horizontal, middle is via, and last is always
vertical. vertical.
""" """
self.layers = layers self.layers = layers
self.rail_track_width = rail_track_width
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers (self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
# This is the minimum routed track spacing # This is the minimum routed track spacing
via_connect = contact(self.layers, (1, 1)) via_connect = contact(self.layers, (1, 1))
@ -24,9 +26,9 @@ class router_tech:
self.horiz_layer_number = layer[self.horiz_layer_name] self.horiz_layer_number = layer[self.horiz_layer_name]
self.vert_layer_number = layer[self.vert_layer_name] self.vert_layer_number = layer[self.vert_layer_name]
if supply_router: if self.rail_track_width>1:
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1,2) (self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0,2) (self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
# For supplies, we will make the wire wider than the vias # For supplies, we will make the wire wider than the vias
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size) self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
@ -45,13 +47,17 @@ class router_tech:
# We'll keep horizontal and vertical tracks the same for simplicity. # We'll keep horizontal and vertical tracks the same for simplicity.
self.track_width = max(self.horiz_track_width,self.vert_track_width) self.track_width = max(self.horiz_track_width,self.vert_track_width)
debug.info(1,"Track width: "+str(self.track_width)) debug.info(1,"Track width: "+str(self.track_width))
self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing)
debug.info(1,"Track spacing: "+str(self.track_space))
self.track_wire = self.track_width - self.track_space
debug.info(1,"Wire width: "+str(self.track_wire))
self.track_widths = vector([self.track_width] * 2) self.track_widths = vector([self.track_width] * 2)
self.track_factor = vector([1/self.track_width] * 2) self.track_factor = vector([1/self.track_width] * 2)
debug.info(2,"Track factor: {0}".format(self.track_factor)) debug.info(2,"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) # 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] self.layer_widths = [self.track_wire, 1, self.track_wire]
def get_zindex(self,layer_num): def get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number: if layer_num==self.horiz_layer_number:
@ -85,7 +91,7 @@ class router_tech:
return (min_width,min_spacing) return (min_width,min_spacing)
def get_supply_layer_width_space(self, zindex, widths=2): def get_supply_layer_width_space(self, zindex):
""" """
These are the width and spacing of a supply layer given a supply rail These are the width and spacing of a supply layer given a supply rail
of the given number of min wire widths. of the given number of min wire widths.
@ -97,10 +103,10 @@ class router_tech:
else: else:
debug.error("Invalid zindex for track", -1) debug.error("Invalid zindex for track", -1)
wire_width = widths*drc("minwidth_{0}".format(layer_name), 0, math.inf) min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
min_width = drc("minwidth_{0}".format(layer_name), wire_width, math.inf) min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), wire_width, math.inf) min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf)
return (min_width,min_spacing) return (min_width,min_spacing)

View File

@ -24,17 +24,16 @@ class supply_router(router):
This will route on layers in design. It will get the blockages from This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file). either the gds file name or the design itself (by saving to a gds file).
""" """
router.__init__(self, layers, design, gds_filename) # Power rail width in minimum wire widths
self.rail_track_width = 3
router.__init__(self, layers, design, gds_filename, self.rail_track_width)
# The list of supply rails (grid sets) that may be routed # The list of supply rails (grid sets) that may be routed
self.supply_rails = {} self.supply_rails = {}
self.supply_rail_wires = {}
# This is the same as above but as a sigle set for the all the rails # This is the same as above but as a sigle set for the all the rails
self.supply_rail_tracks = {} self.supply_rail_tracks = {}
self.supply_rail_wire_tracks = {}
# Power rail width in grid units.
self.rail_track_width = 2
@ -116,9 +115,7 @@ class supply_router(router):
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name)) debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
# These are the wire tracks # These are the wire tracks
wire_tracks = self.supply_rail_wire_tracks[pin_name] wire_tracks = self.supply_rail_tracks[pin_name]
# These are the wire and space tracks
supply_tracks = self.supply_rail_tracks[pin_name]
for pg in self.pin_groups[pin_name]: for pg in self.pin_groups[pin_name]:
if pg.is_routed(): if pg.is_routed():
@ -129,13 +126,10 @@ class supply_router(router):
if len(overlap_grids)>0: if len(overlap_grids)>0:
pg.set_routed() pg.set_routed()
continue continue
# Else, if we overlap some of the space track, we can patch it with an enclosure
common_set = supply_tracks & pg.grids
if len(common_set)>0:
pg.create_simple_overlap_enclosure(common_set)
pg.add_enclosure(self.cell)
# Else, if we overlap some of the space track, we can patch it with an enclosure
#pg.create_simple_overlap_enclosure(pg.grids)
#pg.add_enclosure(self.cell)
@ -146,7 +140,7 @@ class supply_router(router):
NOTE: It is still possible though unlikely that there are disconnected groups of rails. NOTE: It is still possible though unlikely that there are disconnected groups of rails.
""" """
all_rails = self.supply_rail_wires[name] all_rails = self.supply_rails[name]
connections = set() connections = set()
via_areas = [] via_areas = []
@ -186,8 +180,8 @@ class supply_router(router):
# the indices to determine a rail is connected to another # the indices to determine a rail is connected to another
# the overlap area for placement of a via # the overlap area for placement of a via
overlap = new_r1 & new_r2 overlap = new_r1 & new_r2
if len(overlap) >= self.supply_rail_wire_width**2: if len(overlap) >= 1:
debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap)) debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap))
connections.update([i1,i2]) connections.update([i1,i2])
via_areas.append(overlap) via_areas.append(overlap)
@ -196,7 +190,7 @@ class supply_router(router):
ll = grid_utils.get_lower_left(area) ll = grid_utils.get_lower_left(area)
ur = grid_utils.get_upper_right(area) ur = grid_utils.get_upper_right(area)
center = (ll + ur).scale(0.5,0.5,0) center = (ll + ur).scale(0.5,0.5,0)
self.add_via(center,self.rail_track_width) self.add_via(center,1)
# Determien which indices were not connected to anything above # Determien which indices were not connected to anything above
missing_indices = set([x for x in range(len(self.supply_rails[name]))]) missing_indices = set([x for x in range(len(self.supply_rails[name]))])
@ -209,7 +203,6 @@ class supply_router(router):
ur = grid_utils.get_upper_right(all_rails[rail_index]) ur = grid_utils.get_upper_right(all_rails[rail_index])
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur)) debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
self.supply_rails[name].pop(rail_index) self.supply_rails[name].pop(rail_index)
self.supply_rail_wires[name].pop(rail_index)
# Make the supply rails into a big giant set of grids for easy blockages. # Make the supply rails into a big giant set of grids for easy blockages.
# Must be done after we determine which ones are connected. # Must be done after we determine which ones are connected.
@ -226,7 +219,7 @@ class supply_router(router):
ll = grid_utils.get_lower_left(rail) ll = grid_utils.get_lower_left(rail)
ur = grid_utils.get_upper_right(rail) ur = grid_utils.get_upper_right(rail)
z = ll.z z = ll.z
pin = self.compute_wide_enclosure(ll, ur, z, name) pin = self.compute_pin_enclosure(ll, ur, z, name)
debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
self.cell.add_layout_pin(text=name, self.cell.add_layout_pin(text=name,
layer=pin.layer, layer=pin.layer,
@ -242,33 +235,30 @@ class supply_router(router):
self.max_yoffset = self.rg.ur.y self.max_yoffset = self.rg.ur.y
self.max_xoffset = self.rg.ur.x self.max_xoffset = self.rg.ur.x
# Longest length is conservative # # Longest length is conservative
rail_length = max(self.max_yoffset,self.max_xoffset) # rail_length = max(self.max_yoffset,self.max_xoffset)
# Convert the number of tracks to dimensions to get the design rule spacing # # Convert the number of tracks to dimensions to get the design rule spacing
rail_width = self.track_width*self.rail_track_width # rail_width = self.track_width*self.rail_track_width
# Get the conservative width and spacing of the top rails # # Get the conservative width and spacing of the top rails
(horizontal_width, horizontal_space) = self.get_supply_layer_width_space(0,2) # (horizontal_width, horizontal_space) = self.get_supply_layer_width_space(0)
(vertical_width, vertical_space) = self.get_supply_layer_width_space(1,2) # (vertical_width, vertical_space) = self.get_supply_layer_width_space(1)
width = max(horizontal_width, vertical_width) # width = max(horizontal_width, vertical_width)
space = max(horizontal_space, vertical_space) # space = max(horizontal_space, vertical_space)
# This is the supply rail pitch in terms of routing grids # track_pitch = width + space
# i.e. a rail of self.rail_track_width needs this many tracks including
# space
track_pitch = self.rail_track_width*width + space
# Determine the pitch (in tracks) of the rail wire + spacing # # Determine the pitch (in tracks) of the rail wire + spacing
self.supply_rail_width = math.ceil(track_pitch/self.track_width) # self.supply_rail_width = math.ceil(track_pitch/self.track_width)
debug.info(1,"Rail step: {}".format(self.supply_rail_width)) # debug.info(1,"Rail step: {}".format(self.supply_rail_width))
# Conservatively determine the number of tracks that the rail actually occupies # # Conservatively determine the number of tracks that the rail actually occupies
space_tracks = math.ceil(space/self.track_width) # space_tracks = math.ceil(space/self.track_width)
self.supply_rail_wire_width = self.supply_rail_width - space_tracks # self.supply_rail_wire_width = self.supply_rail_width - space_tracks
debug.info(1,"Rail wire tracks: {}".format(self.supply_rail_wire_width)) # debug.info(1,"Rail wire tracks: {}".format(self.supply_rail_wire_width))
total_space = self.supply_rail_width - self.supply_rail_wire_width # total_space = self.supply_rail_width - self.supply_rail_wire_width
self.supply_rail_space_width = math.floor(0.5*total_space) # self.supply_rail_space_width = math.floor(0.5*total_space)
debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width)) # debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width))
def compute_supply_rails(self, name, supply_number): def compute_supply_rails(self, name, supply_number):
@ -279,14 +269,13 @@ class supply_router(router):
""" """
self.supply_rails[name]=[] self.supply_rails[name]=[]
self.supply_rail_wires[name]=[]
start_offset = supply_number*self.supply_rail_width start_offset = supply_number
# Horizontal supply rails # Horizontal supply rails
for offset in range(start_offset, self.max_yoffset, 2*self.supply_rail_width): for offset in range(start_offset, self.max_yoffset, 2):
# Seed the function at the location with the given width # Seed the function at the location with the given width
wave = [vector3d(0,offset+i,0) for i in range(self.supply_rail_width)] wave = [vector3d(0,offset,0)]
# While we can keep expanding east in this horizontal track # While we can keep expanding east in this horizontal track
while wave and wave[0].x < self.max_xoffset: while wave and wave[0].x < self.max_xoffset:
added_rail = self.find_supply_rail(name, wave, direction.EAST) added_rail = self.find_supply_rail(name, wave, direction.EAST)
@ -299,9 +288,9 @@ class supply_router(router):
# Vertical supply rails # Vertical supply rails
max_offset = self.rg.ur.x max_offset = self.rg.ur.x
for offset in range(start_offset, self.max_xoffset, 2*self.supply_rail_width): for offset in range(start_offset, self.max_xoffset, 2):
# Seed the function at the location with the given width # Seed the function at the location with the given width
wave = [vector3d(offset+i,0,1) for i in range(self.supply_rail_width)] wave = [vector3d(offset,0,1)]
# While we can keep expanding north in this vertical track # While we can keep expanding north in this vertical track
while wave and wave[0].y < self.max_yoffset: while wave and wave[0].y < self.max_yoffset:
added_rail = self.find_supply_rail(name, wave, direction.NORTH) added_rail = self.find_supply_rail(name, wave, direction.NORTH)
@ -378,11 +367,6 @@ class supply_router(router):
if len(wave_path)>=4*self.rail_track_width: if len(wave_path)>=4*self.rail_track_width:
grid_set = wave_path.get_grids() grid_set = wave_path.get_grids()
self.supply_rails[name].append(grid_set) self.supply_rails[name].append(grid_set)
start_wire_index = self.supply_rail_space_width
end_wire_index = self.supply_rail_width - self.supply_rail_space_width
wire_set = wave_path.get_wire_grids(start_wire_index,end_wire_index)
self.supply_rail_wires[name].append(wire_set)
return True return True
return False return False
@ -417,10 +401,6 @@ class supply_router(router):
rail_set.update(rail) rail_set.update(rail)
self.supply_rail_tracks[pin_name] = rail_set self.supply_rail_tracks[pin_name] = rail_set
wire_set = set()
for rail in self.supply_rail_wires[pin_name]:
wire_set.update(rail)
self.supply_rail_wire_tracks[pin_name] = wire_set
def route_pins_to_rails(self, pin_name): def route_pins_to_rails(self, pin_name):
@ -465,7 +445,7 @@ class supply_router(router):
""" """
debug.info(4,"Add supply rail target {}".format(pin_name)) debug.info(4,"Add supply rail target {}".format(pin_name))
# Add the wire itself as the target # Add the wire itself as the target
self.rg.set_target(self.supply_rail_wire_tracks[pin_name]) self.rg.set_target(self.supply_rail_tracks[pin_name])
# But unblock all the rail tracks including the space # But unblock all the rail tracks including the space
self.rg.set_blocked(self.supply_rail_tracks[pin_name],False) self.rg.set_blocked(self.supply_rail_tracks[pin_name],False)

View File

@ -22,6 +22,9 @@ class no_blockages_test(openram_test):
if False: if False:
from control_logic import control_logic from control_logic import control_logic
cell = control_logic(16) cell = control_logic(16)
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
else: else:
from sram import sram from sram import sram
from sram_config import sram_config from sram_config import sram_config
@ -33,9 +36,6 @@ class no_blockages_test(openram_test):
sram = sram(c, "sram1") sram = sram(c, "sram1")
cell = sram.s cell = sram.s
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
self.local_check(cell,True) self.local_check(cell,True)
# fails if there are any DRC errors on any cells # fails if there are any DRC errors on any cells