Merge branch 'dev' into datasheet_gen

This commit is contained in:
Jesse Cirimelli-Low 2018-12-04 16:27:04 -08:00
commit b6e7ddd023
14 changed files with 355 additions and 315 deletions

View File

@ -441,10 +441,10 @@ class pin_layout:
"""
Given three co-linear points, determine if q lies on segment pr
"""
if q[0] <= max(p[0], r[0]) and \
q[0] >= min(p[0], r[0]) and \
q[1] <= max(p[1], r[1]) and \
q[1] >= min(p[1], r[1]):
if q.x <= max(p.x, r.x) and \
q.x >= min(p.x, r.x) and \
q.y <= max(p.y, r.y) and \
q.y >= min(p.y, r.y):
return True
return False
@ -473,8 +473,8 @@ class pin_layout:
x = (b2*c1 - b1*c2)/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):
return [x, y]
return r
return None

View File

@ -172,7 +172,6 @@ class pin_group:
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()
@ -183,7 +182,6 @@ class pin_group:
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()
@ -194,8 +192,10 @@ class pin_group:
ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin)
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
def find_above_connector(self, pin, enclosures):
@ -226,7 +226,7 @@ class pin_group:
# If it already overlaps, no connector needed
if above_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, above_item)
return p
@ -485,7 +485,9 @@ class pin_group:
for pin_list in self.pins:
if not self.overlap_any_shape(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)
@ -559,15 +561,13 @@ class pin_group:
of any grid in the other set.
"""
# We could optimize this to just check the boundaries
g1_grids = set()
g2_grids = set()
adj_grids = set()
for g1 in self.grids:
for g2 in other.grids:
if g1.distance(g2) <= separation:
g1_grids.add(g1)
g2_grids.add(g2)
adj_grids.add(g1)
return g1_grids,g2_grids
return adj_grids
def convert_pin(self):
"""
@ -576,14 +576,16 @@ class pin_group:
should be either blocked or part of the pin.
"""
pin_set = set()
partial_set = set()
blockage_set = set()
for pin_list in self.pins:
for pin in pin_list:
debug.info(2," Converting {0}".format(pin))
# Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(pin_in_tracks)
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(sufficient)
partial_set.update(insufficient)
# Blockages will be a super-set of pins since it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin)
@ -595,89 +597,94 @@ class pin_group:
if len(shared_set)>0:
debug.info(2,"Removing pins {}".format(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
if len(shared_set)>0:
debug.info(2,"Removing blocks {}".format(shared_set))
blockage_set.difference_update(shared_set)
# 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))
for pin_list in self.pins:
for pin in pin_list:
debug.info(2," Converting {0}".format(pin))
debug.warning(" Expanding conversion {0}".format(pin))
# Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
pin_set.update(pin_in_tracks)
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
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))
self.router.write_debug_gds("blocked_pin.gds")
# We need to route each of the components, so don't combine the groups
self.grids = pin_set | blockage_set
# Remember the secondary grids for removing adjacent pins in wide metal spacing
self.secondary_grids = blockage_set - pin_set
# Consider all the grids that would be blocked
self.grids = pin_set | partial_set
# Remember the secondary grids for removing adjacent pins
self.secondary_grids = partial_set
debug.info(2," pins {}".format(self.grids))
debug.info(2," secondary {}".format(self.secondary_grids))
def recurse_simple_overlap_enclosure(self, start_set, direct):
"""
Recursive function to return set of tracks that connects to
the actual supply rail wire in a given direction (or terminating
when any track is no longer in the supply rail.
"""
next_set = grid_utils.expand_border(start_set, direct)
# def recurse_simple_overlap_enclosure(self, start_set, direct):
# """
# Recursive function to return set of tracks that connects to
# the actual supply rail wire in a given direction (or terminating
# when any track is no longer in the supply rail.
# """
# next_set = grid_utils.expand_border(start_set, direct)
supply_tracks = self.router.supply_rail_tracks[self.name]
supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
# supply_tracks = self.router.supply_rail_tracks[self.name]
# supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
supply_overlap = next_set & supply_tracks
wire_overlap = next_set & supply_wire_tracks
# supply_overlap = next_set & supply_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 len(wire_overlap)==len(start_set):
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
elif len(supply_overlap)==len(start_set):
recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
new_set = start_set | supply_overlap | recurse_set
else:
# If we got no next set, we are done, can't expand!
new_set = set()
# # If the rail overlap is the same, we are done, since we connected to the actual wire
# if len(wire_overlap)==len(start_set):
# 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
# elif len(supply_overlap)==len(start_set):
# recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
# new_set = start_set | supply_overlap | recurse_set
# else:
# # If we got no next set, we are done, can't expand!
# new_set = set()
return new_set
# return new_set
def create_simple_overlap_enclosure(self, start_set):
"""
This takes a set of tracks that overlap a supply rail and creates an enclosure
that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure.
"""
additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set))
new_set = start_set.copy()
if e.z==0:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
else:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
# def create_simple_overlap_enclosure(self, start_set):
# """
# This takes a set of tracks that overlap a supply rail and creates an enclosure
# that is ensured to overlap the supply rail wire.
# It then adds rectangle(s) for the enclosure.
# """
# additional_set = set()
# # Check the layer of any element in the pin to determine which direction to route it
# e = next(iter(start_set))
# new_set = start_set.copy()
# if e.z==0:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
# if not new_set:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
# else:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
# if not new_set:
# 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
self.grids.update(new_set)
# # Expand the pin grid set to include some extra grids that connect the supply rail
# self.grids.update(new_set)
# Add the inflated set so we don't get wide metal spacing issues (if it exists)
self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width))
# # Block the grids
# self.blockages.update(new_set)
# Add the polygon enclosures and set this pin group as routed
self.set_routed()
self.enclosures = self.compute_enclosures()
# # Add the polygon enclosures and set this pin group as routed
# self.set_routed()
# self.enclosures = self.compute_enclosures()

View File

@ -21,12 +21,12 @@ class router(router_tech):
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
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
@ -177,15 +177,15 @@ class router(router_tech):
#print_time("Convert pins",datetime.now(), start_time)
#start_time = datetime.now()
for pin in pin_list:
self.combine_adjacent_pins(pin)
#for pin in pin_list:
# self.combine_adjacent_pins(pin)
#print_time("Combine pins",datetime.now(), start_time)
#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 that overlap
# Must be done before enclosing pins
#start_time = datetime.now()
self.separate_adjacent_pins(self.supply_rail_space_width)
self.separate_adjacent_pins(0)
#print_time("Separate pins",datetime.now(), start_time)
# For debug
#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
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))
# Find all adjacencies
@ -279,48 +279,50 @@ class router(router_tech):
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
for index2,pg2 in enumerate(self.pin_groups[pin_name2]):
# FIgXME: Use separation distance and edge grids only
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
adj_grids = pg1.adjacent_grids(pg2, separation)
# These should have the same length, so...
if len(grids_g1)>0:
debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2)
if len(adj_grids)>0:
debug.info(2,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids))
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.
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():
bigger = pg1
bigger_grids = grids1
smaller = pg2
smaller_grids = grids2
else:
bigger = pg2
bigger_grids = grids2
smaller = pg1
smaller_grids = grids1
# First, see if we can remove grids that are in the secondary 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
for adj in adj_grids:
# If that fails, just randomly remove from the bigger one and give a warning.
# This might fail later.
debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids))
debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids))
bigger.grids.difference_update(bigger_grids)
self.blocked_grids.update(bigger_grids)
# If the adjacent grids are a subset of the secondary grids (i.e. not necessary)
# remove them from each
if adj in bigger.secondary_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)
elif adj in smaller.secondary_grids:
debug.info(2,"Removing {} from smaller secondary {}".format(adj, smaller))
smaller.grids.remove(adj)
smaller.secondary_grids.remove(adj)
self.blocked_grids.add(adj)
else:
# If we couldn't remove from a secondary grid, we must remove from the primary
# grid of at least one pin
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)
@ -515,36 +517,24 @@ class router(router_tech):
# scale the size bigger to include neaby tracks
ll=ll.scale(self.track_factor).floor()
ur=ur.scale(self.track_factor).ceil()
#print(pin)
# Keep tabs on tracks with sufficient and insufficient overlap
sufficient_list = set()
insufficient_list = set()
zindex=self.get_zindex(pin.layer_num)
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):
(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:
sufficient_list.update([full_overlap])
if 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(2,"Converting [ {0} , {1} ] full={2}".format(x,y, full_overlap))
# 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()
# Return all grids with any potential overlap (sufficient or not)
return (sufficient_list,insufficient_list)
def get_all_offgrid_pin(self, pin, insufficient_list):
"""
@ -622,26 +612,32 @@ class router(router_tech):
def convert_pin_coord_to_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.
Return all tracks that an inflated pin overlaps
"""
(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)
# This is using the full track shape rather than a single track pin shape
# because we will later patch a connector if there isn't overlap.
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))
# 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.
overlap_length = pin.overlap_length(track_pin)
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
inflated_overlap_length = inflated_pin.overlap_length(track_pin)
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length))
# If it overlaps with the pin, it is sufficient
if 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:
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing))
return (None, coord)
debug.info(2," No overlap: {0} {1}".format(overlap_length,0))
return (None,None)
@ -653,25 +649,34 @@ class router(router_tech):
Convert a grid point into a rectangle shape that is centered
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
x = track.x*self.track_width - 0.5*self.track_width + space
y = track.y*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 + 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 - space
y = track.y*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 - 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 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):
"""
Convert a grid point into a rectangle shape that occupies the entire centered
@ -686,6 +691,23 @@ class router(router_tech):
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):
"""
Analyze the shapes of a pin and combine them into groups which are connected.
@ -877,8 +899,6 @@ class router(router_tech):
Enclose the tracks from ll to ur in a single rectangle that meets
the track DRC rules.
"""
# Get the layer information
(width, space) = self.get_layer_width_space(zindex)
layer = self.get_layer(zindex)
# This finds the pin shape enclosed by the track with DRC spacing on the sides
@ -894,35 +914,35 @@ class router(router_tech):
return pin
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.
"""
# 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.
# """
# Find the pin enclosure of the whole track shape (ignoring DRCs)
(abs_ll,unused) = self.convert_track_to_shape(ll)
(unused,abs_ur) = self.convert_track_to_shape(ur)
# # Find the pin enclosure of the whole track shape (ignoring DRCs)
# (abs_ll,unused) = self.convert_track_to_shape(ll)
# (unused,abs_ur) = self.convert_track_to_shape(ur)
# Get the layer information
x_distance = abs(abs_ll.x-abs_ur.x)
y_distance = abs(abs_ll.y-abs_ur.y)
shape_width = min(x_distance, y_distance)
shape_length = max(x_distance, y_distance)
# # Get the layer information
# x_distance = abs(abs_ll.x-abs_ur.x)
# y_distance = abs(abs_ll.y-abs_ur.y)
# shape_width = min(x_distance, y_distance)
# shape_length = max(x_distance, y_distance)
# Get the DRC rule for the grid dimensions
(width, space) = self.get_layer_width_space(zindex, shape_width, shape_length)
layer = self.get_layer(zindex)
# # Get the DRC rule for the grid dimensions
# (width, space) = self.get_supply_layer_width_space(zindex)
# layer = self.get_layer(zindex)
if zindex==0:
spacing = vector(0.5*self.track_width, 0.5*space)
else:
spacing = vector(0.5*space, 0.5*self.track_width)
# Compute the shape offsets with correct spacing
new_ll = abs_ll + spacing
new_ur = abs_ur - spacing
pin = pin_layout(name, [new_ll, new_ur], layer)
# if zindex==0:
# spacing = vector(0.5*self.track_width, 0.5*space)
# else:
# spacing = vector(0.5*space, 0.5*self.track_width)
# # Compute the shape offsets with correct spacing
# new_ll = abs_ll + spacing
# new_ur = abs_ur - spacing
# pin = pin_layout(name, [new_ll, new_ur], layer)
return pin
# return pin
def contract_path(self,path):
@ -963,8 +983,7 @@ class router(router_tech):
self.add_route(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(inflated_path)
self.path_blockages.append(path_set)
else:
self.write_debug_gds("failed_route.gds")
# clean up so we can try a reroute

View File

@ -3,48 +3,62 @@ from contact import contact
from pin_group import pin_group
from vector import vector
import debug
import math
class router_tech:
"""
This is a class to hold the router tech constants.
"""
def __init__(self, layers):
def __init__(self, layers, rail_track_width):
"""
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.rail_track_width = rail_track_width
(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))
max_via_size = max(via_connect.width,via_connect.height)
self.horiz_layer_number = layer[self.horiz_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
if self.rail_track_width>1:
(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)
# For supplies, we will make the wire wider than the vias
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size)
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
else:
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
self.horiz_track_width = max_via_size + self.horiz_layer_spacing
self.vert_track_width = 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))
debug.info(1,"Track width: {:.3f}".format(self.track_width))
self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing)
debug.info(1,"Track spacing: {:.3f}".format(self.track_space))
self.track_wire = self.track_width - self.track_space
debug.info(1,"Wire width: {:.3f}".format(self.track_wire))
self.track_widths = vector([self.track_width] * 2)
self.track_factor = vector([1/self.track_width] * 2)
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)
self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
debug.info(2,"Track factor: {}".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_wire, 1, self.track_wire]
def get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number:
return 0
@ -76,4 +90,24 @@ class router_tech:
return (min_width,min_spacing)
def get_supply_layer_width_space(self, zindex):
"""
These are the width and spacing of a supply layer given a supply rail
of the given number of min wire widths.
"""
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_wire_width = drc("minwidth_{0}".format(layer_name), 0, 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), self.rail_track_width*min_wire_width, math.inf)
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
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
self.supply_rails = {}
self.supply_rail_wires = {}
# This is the same as above but as a sigle set for the all the rails
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))
# These are the wire tracks
wire_tracks = self.supply_rail_wire_tracks[pin_name]
# These are the wire and space tracks
supply_tracks = self.supply_rail_tracks[pin_name]
wire_tracks = self.supply_rail_tracks[pin_name]
for pg in self.pin_groups[pin_name]:
if pg.is_routed():
@ -129,13 +126,10 @@ class supply_router(router):
if len(overlap_grids)>0:
pg.set_routed()
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.
"""
all_rails = self.supply_rail_wires[name]
all_rails = self.supply_rails[name]
connections = set()
via_areas = []
@ -186,8 +180,8 @@ class supply_router(router):
# the indices to determine a rail is connected to another
# the overlap area for placement of a via
overlap = new_r1 & new_r2
if len(overlap) >= self.supply_rail_wire_width**2:
debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap))
if len(overlap) >= 1:
debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap))
connections.update([i1,i2])
via_areas.append(overlap)
@ -196,7 +190,7 @@ class supply_router(router):
ll = grid_utils.get_lower_left(area)
ur = grid_utils.get_upper_right(area)
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
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])
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
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.
# 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)
ur = grid_utils.get_upper_right(rail)
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))
self.cell.add_layout_pin(text=name,
layer=pin.layer,
@ -242,33 +235,30 @@ class supply_router(router):
self.max_yoffset = self.rg.ur.y
self.max_xoffset = self.rg.ur.x
# Longest length is conservative
rail_length = max(self.max_yoffset,self.max_xoffset)
# Convert the number of tracks to dimensions to get the design rule spacing
rail_width = self.track_width*self.rail_track_width
# # Longest length is conservative
# rail_length = max(self.max_yoffset,self.max_xoffset)
# # Convert the number of tracks to dimensions to get the design rule spacing
# rail_width = self.track_width*self.rail_track_width
# Get the conservative width and spacing of the top rails
(horizontal_width, horizontal_space) = self.get_layer_width_space(0, rail_width, rail_length)
(vertical_width, vertical_space) = self.get_layer_width_space(1, rail_width, rail_length)
width = max(horizontal_width, vertical_width)
space = max(horizontal_space, vertical_space)
# # Get the conservative width and spacing of the top rails
# (horizontal_width, horizontal_space) = self.get_supply_layer_width_space(0)
# (vertical_width, vertical_space) = self.get_supply_layer_width_space(1)
# width = max(horizontal_width, vertical_width)
# space = max(horizontal_space, vertical_space)
# This is the supply rail pitch in terms of routing grids
# i.e. a rail of self.rail_track_width needs this many tracks including
# space
track_pitch = self.rail_track_width*width + space
# track_pitch = width + space
# Determine the pitch (in tracks) of the rail wire + spacing
self.supply_rail_width = math.ceil(track_pitch/self.track_width)
debug.info(1,"Rail step: {}".format(self.supply_rail_width))
# # Determine the pitch (in tracks) of the rail wire + spacing
# self.supply_rail_width = math.ceil(track_pitch/self.track_width)
# debug.info(1,"Rail step: {}".format(self.supply_rail_width))
# Conservatively determine the number of tracks that the rail actually occupies
space_tracks = math.ceil(space/self.track_width)
self.supply_rail_wire_width = self.supply_rail_width - space_tracks
debug.info(1,"Rail wire tracks: {}".format(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)
debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width))
# # Conservatively determine the number of tracks that the rail actually occupies
# space_tracks = math.ceil(space/self.track_width)
# self.supply_rail_wire_width = self.supply_rail_width - space_tracks
# debug.info(1,"Rail wire tracks: {}".format(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)
# debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width))
def compute_supply_rails(self, name, supply_number):
@ -279,14 +269,13 @@ class supply_router(router):
"""
self.supply_rails[name]=[]
self.supply_rail_wires[name]=[]
start_offset = supply_number*self.supply_rail_width
start_offset = supply_number
# 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
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 wave and wave[0].x < self.max_xoffset:
added_rail = self.find_supply_rail(name, wave, direction.EAST)
@ -299,9 +288,9 @@ class supply_router(router):
# Vertical supply rails
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
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 wave and wave[0].y < self.max_yoffset:
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:
grid_set = wave_path.get_grids()
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 False
@ -417,10 +401,6 @@ class supply_router(router):
rail_set.update(rail)
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):
@ -465,7 +445,7 @@ class supply_router(router):
"""
debug.info(4,"Add supply rail target {}".format(pin_name))
# 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
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:
from control_logic import control_logic
cell = control_logic(16)
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
else:
from sram import sram
from sram_config import sram_config
@ -33,9 +36,6 @@ class no_blockages_test(openram_test):
sram = sram(c, "sram1")
cell = sram.s
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
self.local_check(cell,True)
# fails if there are any DRC errors on any cells

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
class psram_1bank_2mux_test(openram_test):
def runTest(self):

View File

@ -29,13 +29,13 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
num_banks=1)
c.words_per_row=2
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,13 +23,13 @@ class sram_1bank_2mux_test(openram_test):
num_banks=1)
c.words_per_row=2
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,13 +23,13 @@ class sram_1bank_4mux_test(openram_test):
num_banks=1)
c.words_per_row=4
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -29,13 +29,13 @@ class sram_1bank_8mux_1rw_1r_test(openram_test):
num_banks=1)
c.words_per_row=8
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,13 +23,13 @@ class sram_1bank_8mux_test(openram_test):
num_banks=1)
c.words_per_row=8
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -29,13 +29,13 @@ class sram_1bank_nomux_1rw_1r_test(openram_test):
num_banks=1)
c.words_per_row=1
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,13 +23,13 @@ class sram_1bank_nomux_test(openram_test):
num_banks=1)
c.words_per_row=1
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)