mirror of https://github.com/VLSIDA/OpenRAM.git
DRC clean supply grid routing on control logic.
This commit is contained in:
parent
fd9ffe30d6
commit
87502374c5
|
|
@ -63,7 +63,7 @@ class pin_layout:
|
|||
and return the new rectangle.
|
||||
"""
|
||||
if not spacing:
|
||||
spacing = drc["{0}_to_{0}".format(self.layer)]
|
||||
spacing = 0.5*drc["{0}_to_{0}".format(self.layer)]
|
||||
|
||||
(ll,ur) = self.rect
|
||||
spacing = vector(spacing, spacing)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import sys
|
||||
import gdsMill
|
||||
import tech
|
||||
from contact import contact
|
||||
|
|
@ -36,18 +37,26 @@ class router:
|
|||
self.reader.loadFromFile(gds_name)
|
||||
self.top_name = self.layout.rootStructureName
|
||||
|
||||
### The pin data structures
|
||||
# A map of pin names to pin structures
|
||||
self.pins = {}
|
||||
# This is a set of all pins so that we don't create blockages for these shapes.
|
||||
self.all_pins = set()
|
||||
# A set of connected pin groups
|
||||
self.pin_groups = {}
|
||||
# The corresponding sets (components) of grids for each pin
|
||||
self.pin_components = {}
|
||||
|
||||
### The blockage data structures
|
||||
# A list of pin layout shapes that are blockages
|
||||
self.blockages=[]
|
||||
# A set of blocked grids
|
||||
self.blocked_grids = set()
|
||||
# The corresponding set of partially blocked grids for each component.
|
||||
# These are blockages for other nets but unblocked for this component.
|
||||
self.pin_component_blockages = {}
|
||||
|
||||
# A list of pin layout shapes that are blocked
|
||||
self.blockages=[]
|
||||
|
||||
### The routed data structures
|
||||
# A list of paths that have been "routed"
|
||||
self.paths = []
|
||||
# The list of supply rails that may be routed
|
||||
|
|
@ -65,10 +74,13 @@ class router:
|
|||
Keep the other blockages unchanged.
|
||||
"""
|
||||
self.pins = {}
|
||||
self.all_pins = set()
|
||||
self.pin_groups = {}
|
||||
self.pin_grids = {}
|
||||
self.pin_paritals = {}
|
||||
self.reinit()
|
||||
# DO NOT clear the blockages as these don't change
|
||||
self.rg.reinit()
|
||||
|
||||
|
||||
def set_top(self,top_name):
|
||||
""" If we want to route something besides the top-level cell."""
|
||||
|
|
@ -103,11 +115,11 @@ class router:
|
|||
self.layers = layers
|
||||
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
|
||||
|
||||
self.vert_layer_width = tech.drc["minwidth_{0}".format(self.vert_layer_name)]
|
||||
self.vert_layer_minwidth = tech.drc["minwidth_{0}".format(self.vert_layer_name)]
|
||||
self.vert_layer_spacing = tech.drc[str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name)]
|
||||
self.vert_layer_number = tech.layer[self.vert_layer_name]
|
||||
|
||||
self.horiz_layer_width = tech.drc["minwidth_{0}".format(self.horiz_layer_name)]
|
||||
self.horiz_layer_minwidth = tech.drc["minwidth_{0}".format(self.horiz_layer_name)]
|
||||
self.horiz_layer_spacing = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)]
|
||||
self.horiz_layer_number = tech.layer[self.horiz_layer_name]
|
||||
|
||||
|
|
@ -143,6 +155,7 @@ class router:
|
|||
|
||||
debug.check(len(pin_set)>0,"Did not find any pin shapes for {0}.".format(str(pin_name)))
|
||||
self.pins[pin_name] = pin_set
|
||||
self.all_pins.update(pin_set)
|
||||
|
||||
def find_pins(self,pin_name):
|
||||
"""
|
||||
|
|
@ -151,7 +164,6 @@ class router:
|
|||
"""
|
||||
self.retrieve_pins(pin_name)
|
||||
self.analyze_pins(pin_name)
|
||||
self.convert_pins(pin_name)
|
||||
|
||||
|
||||
def find_blockages(self):
|
||||
|
|
@ -165,18 +177,16 @@ class router:
|
|||
|
||||
self.convert_blockages()
|
||||
|
||||
def reinit(self):
|
||||
"""
|
||||
Reset the source and destination pins to start a new routing.
|
||||
Convert the source/dest pins to blockages.
|
||||
Convert the routed path to blockages.
|
||||
Keep the other blockages unchanged.
|
||||
"""
|
||||
self.pins = {}
|
||||
self.pin_groups = {}
|
||||
self.pin_components = {}
|
||||
# DO NOT clear the blockages as these don't change
|
||||
self.rg.reinit()
|
||||
# # def reinit(self):
|
||||
# # """
|
||||
# # Reset the source and destination pins to start a new routing.
|
||||
# # Convert the source/dest pins to blockages.
|
||||
# # Convert the routed path to blockages.
|
||||
# # Keep the other blockages unchanged.
|
||||
# # """
|
||||
# # self.clear_pins()
|
||||
# # # DO NOT clear the blockages as these don't change
|
||||
# # self.rg.reinit()
|
||||
|
||||
|
||||
|
||||
|
|
@ -268,10 +278,10 @@ class router:
|
|||
"""
|
||||
Convert a pin layout blockage shape to routing grid tracks.
|
||||
"""
|
||||
# Inflate the blockage by spacing rule
|
||||
# Inflate the blockage by half a spacing rule
|
||||
[ll,ur]=self.convert_blockage_to_tracks(blockage.inflate())
|
||||
zlayer = self.get_zindex(blockage.layer_num)
|
||||
blockage_tracks = self.get_blockage_tracks(ll,ur,zlayer)
|
||||
blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer)
|
||||
return blockage_tracks
|
||||
|
||||
def convert_blockages(self):
|
||||
|
|
@ -294,7 +304,9 @@ class router:
|
|||
ur = vector(boundary[2],boundary[3])
|
||||
rect = [ll,ur]
|
||||
new_pin = pin_layout("blockage{}".format(len(self.blockages)),rect,layer_num)
|
||||
self.blockages.append(new_pin)
|
||||
# If there is a rectangle that is the same in the pins, it isn't a blockage!
|
||||
if new_pin not in self.all_pins:
|
||||
self.blockages.append(new_pin)
|
||||
|
||||
|
||||
def convert_point_to_units(self, p):
|
||||
|
|
@ -317,8 +329,8 @@ class router:
|
|||
Convert a rectangular blockage shape into track units.
|
||||
"""
|
||||
(ll,ur) = shape
|
||||
# ll = snap_to_grid(ll)
|
||||
# ur = snap_to_grid(ur)
|
||||
ll = snap_to_grid(ll)
|
||||
ur = snap_to_grid(ur)
|
||||
|
||||
# to scale coordinates to tracks
|
||||
debug.info(3,"Converting [ {0} , {1} ]".format(ll,ur))
|
||||
|
|
@ -337,11 +349,10 @@ class router:
|
|||
# debug.info(0,"Pin {}".format(pin))
|
||||
return [ll,ur]
|
||||
|
||||
def convert_pin_to_tracks(self, pin):
|
||||
def convert_pin_to_tracks(self, pin_name, pin):
|
||||
"""
|
||||
Convert a rectangular pin shape into a list of track locations,layers.
|
||||
If no on-grid pins are found, it searches for the nearest off-grid pin(s).
|
||||
If a pin has insufficent overlap, it returns the blockage list to avoid it.
|
||||
If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked.
|
||||
"""
|
||||
(ll,ur) = pin.rect
|
||||
debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur))
|
||||
|
|
@ -350,39 +361,94 @@ class router:
|
|||
ll=ll.scale(self.track_factor).floor()
|
||||
ur=ur.scale(self.track_factor).ceil()
|
||||
|
||||
# width depends on which layer it is
|
||||
# Keep tabs on tracks with sufficient and insufficient overlap
|
||||
sufficient_list = set()
|
||||
insufficient_list = set()
|
||||
|
||||
zindex=self.get_zindex(pin.layer_num)
|
||||
if zindex:
|
||||
width = self.vert_layer_width
|
||||
spacing = self.vert_layer_spacing
|
||||
else:
|
||||
width = self.horiz_layer_width
|
||||
spacing = self.horiz_layer_spacing
|
||||
|
||||
track_list = []
|
||||
block_list = []
|
||||
|
||||
for x in range(int(ll[0]),int(ur[0])+1):
|
||||
for y in range(int(ll[1]),int(ur[1])+1):
|
||||
debug.info(4,"Converting [ {0} , {1} ]".format(x,y))
|
||||
(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])
|
||||
|
||||
# however, if there is not enough overlap, then if there is any overlap at all,
|
||||
# we need to block it to prevent routes coming in on that grid
|
||||
full_rect = self.convert_track_to_shape(vector3d(x,y,zindex))
|
||||
overlap_rect=self.compute_overlap(pin.rect,full_rect)
|
||||
min_overlap = min(overlap_rect)
|
||||
debug.info(3,"Check overlap: {0} . {1} = {2}".format(pin.rect,full_rect,overlap_rect))
|
||||
if len(sufficient_list)>0:
|
||||
return sufficient_list
|
||||
elif len(insufficient_list)>0:
|
||||
# If there wasn't a sufficient grid, find the best and patch it to be on grid.
|
||||
return self.get_best_offgrid_pin(pin, insufficient_list)
|
||||
else:
|
||||
debug.error("Unable to find any overlapping grids.", -1)
|
||||
|
||||
|
||||
def get_best_offgrid_pin(self, pin, insufficient_list):
|
||||
"""
|
||||
Given a pin and a list of partial overlap grids:
|
||||
1) Find the unblocked grids.
|
||||
2) If one, use it.
|
||||
3) If not, find the greatest overlap.
|
||||
4) Add a pin with the most overlap to make it "on grid"
|
||||
that is not blocked.
|
||||
"""
|
||||
#print("INSUFFICIENT LIST",insufficient_list)
|
||||
# Find the coordinate with the most overlap
|
||||
best_coord = None
|
||||
best_overlap = -math.inf
|
||||
for coord in insufficient_list:
|
||||
full_rect = self.convert_track_to_pin(coord)
|
||||
# Compute the overlap with that rectangle
|
||||
overlap_rect=self.compute_overlap(pin.rect,full_rect)
|
||||
# Determine the min x or y overlap
|
||||
min_overlap = min(overlap_rect)
|
||||
if min_overlap>best_overlap:
|
||||
best_overlap=min_overlap
|
||||
best_coord=coord
|
||||
|
||||
return set([best_coord])
|
||||
|
||||
|
||||
def get_layer_width_space(self, zindex):
|
||||
"""
|
||||
Return the width and spacing of a given layer.
|
||||
"""
|
||||
if zindex==1:
|
||||
width = self.vert_layer_minwidth
|
||||
spacing = self.vert_layer_spacing
|
||||
elif zindex==0:
|
||||
width = self.horiz_layer_minwidth
|
||||
spacing = self.horiz_layer_spacing
|
||||
else:
|
||||
debug.error("Invalid zindex for track", -1)
|
||||
|
||||
return (width,spacing)
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
(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_rect = self.convert_track_to_pin(coord)
|
||||
overlap_width = self.compute_overlap_width(pin.rect, track_rect)
|
||||
|
||||
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_rect, overlap_width))
|
||||
# If it overlaps by more than the min width DRC, we can just use the track
|
||||
if overlap_width==math.inf or snap_val_to_grid(overlap_width) >= snap_val_to_grid(width):
|
||||
debug.info(3," Overlap: {0} >? {1}".format(overlap_width,spacing))
|
||||
return (coord, None)
|
||||
# Otherwise, keep track of the partial overlap grids in case we need to patch it later.
|
||||
else:
|
||||
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_width,spacing))
|
||||
return (None, coord)
|
||||
|
||||
|
||||
if min_overlap > spacing:
|
||||
debug.info(3," Overlap: {0} {1} >? {2}".format(overlap_rect,min_overlap,spacing))
|
||||
track_list.append(vector3d(x,y,zindex))
|
||||
# otherwise, the pin may not be accessible, so block it
|
||||
elif min_overlap > 0:
|
||||
debug.info(3," Insufficient overlap: {0} {1} >? {2}".format(overlap_rect,min_overlap,spacing))
|
||||
block_list.append(vector3d(x,y,zindex))
|
||||
else:
|
||||
debug.info(4," No overlap: {0} {1} max={2}".format(overlap_rect,min_overlap,spacing))
|
||||
return (tuple(track_list),tuple(block_list))
|
||||
|
||||
def compute_overlap(self, r1, r2):
|
||||
""" Calculate the rectangular overlap of two rectangles. """
|
||||
|
|
@ -399,7 +465,116 @@ class router:
|
|||
return [dx,dy]
|
||||
else:
|
||||
return [0,0]
|
||||
|
||||
def compute_overlap_width(self, r1, r2):
|
||||
"""
|
||||
Calculate the intersection segment and determine its width.
|
||||
"""
|
||||
intersections = self.compute_overlap_segment(r1,r2)
|
||||
|
||||
if len(intersections)==2:
|
||||
(p1,p2) = intersections
|
||||
return math.sqrt(pow(p1[0]-p2[0],2) + pow(p1[1]-p2[1],2))
|
||||
else:
|
||||
# we either have no overlap or complete overlap
|
||||
# Compute the width of the overlap of the two rectangles
|
||||
overlap_rect=self.compute_overlap(r1, r2)
|
||||
# Determine the min x or y overlap
|
||||
min_overlap = min(overlap_rect)
|
||||
if min_overlap>0:
|
||||
return math.inf
|
||||
else:
|
||||
return 0
|
||||
|
||||
|
||||
def compute_overlap_segment(self, r1, r2):
|
||||
"""
|
||||
Calculate the intersection segment of two rectangles
|
||||
(if any)
|
||||
"""
|
||||
(r1_ll,r1_ur) = r1
|
||||
(r2_ll,r2_ur) = r2
|
||||
|
||||
# The other corners besides ll and ur
|
||||
r1_ul = vector(r1_ll.x, r1_ur.y)
|
||||
r1_lr = vector(r1_ur.x, r1_ll.y)
|
||||
r2_ul = vector(r2_ll.x, r2_ur.y)
|
||||
r2_lr = vector(r2_ur.x, r2_ll.y)
|
||||
|
||||
from itertools import tee
|
||||
def pairwise(iterable):
|
||||
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
||||
a, b = tee(iterable)
|
||||
next(b, None)
|
||||
return zip(a, b)
|
||||
|
||||
# R1 edges CW
|
||||
r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll]
|
||||
r1_edges = []
|
||||
for (p,q) in pairwise(r1_cw_points):
|
||||
r1_edges.append([p,q])
|
||||
|
||||
# R2 edges CW
|
||||
r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll]
|
||||
r2_edges = []
|
||||
for (p,q) in pairwise(r2_cw_points):
|
||||
r2_edges.append([p,q])
|
||||
|
||||
# There are 4 edges on each rectangle
|
||||
# so just brute force check intersection of each
|
||||
# Two pairs of them should intersect
|
||||
intersections = []
|
||||
for r1e in r1_edges:
|
||||
for r2e in r2_edges:
|
||||
i = self.segment_intersection(r1e, r2e)
|
||||
if i:
|
||||
intersections.append(i)
|
||||
|
||||
return intersections
|
||||
|
||||
def on_segment(self, p, q, r):
|
||||
"""
|
||||
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]):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def segment_intersection(self, s1, s2):
|
||||
"""
|
||||
Determine the intersection point of two segments
|
||||
Return the a segment if they overlap.
|
||||
Return None if they don't.
|
||||
"""
|
||||
(a,b) = s1
|
||||
(c,d) = s2
|
||||
# Line AB represented as a1x + b1y = c1
|
||||
a1 = b.y - a.y
|
||||
b1 = a.x - b.x
|
||||
c1 = a1*a.x + b1*a.y
|
||||
|
||||
# Line CD represented as a2x + b2y = c2
|
||||
a2 = d.y - c.y
|
||||
b2 = c.x - d.x
|
||||
c2 = a2*c.x + b2*c.y
|
||||
|
||||
determinant = a1*b2 - a2*b1
|
||||
|
||||
if determinant!=0:
|
||||
x = (b2*c1 - b1*c2)/determinant
|
||||
y = (a1*c2 - a2*c1)/determinant
|
||||
|
||||
r = [x,y]
|
||||
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
||||
return [x, y]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def convert_track_to_pin(self, track):
|
||||
"""
|
||||
|
|
@ -506,20 +681,25 @@ class router:
|
|||
self.pin_components[pin_name]
|
||||
except:
|
||||
self.pin_components[pin_name] = []
|
||||
|
||||
|
||||
try:
|
||||
self.pin_component_blockages[pin_name]
|
||||
except:
|
||||
self.pin_component_blockages[pin_name] = []
|
||||
|
||||
found_pin = False
|
||||
for pg in self.pin_groups[pin_name]:
|
||||
print("PG ",pg)
|
||||
#print("PG ",pg)
|
||||
# Keep the same groups for each pin
|
||||
pin_set = set()
|
||||
blockage_set = set()
|
||||
for pin in pg:
|
||||
debug.info(2," Converting {0}".format(pin))
|
||||
(pin_in_tracks,partial_in_tracks)=self.convert_pin_to_tracks(pin)
|
||||
blockage_in_tracks = self.convert_blockage(pin)
|
||||
# Determine which tracks the pin overlaps
|
||||
pin_in_tracks=self.convert_pin_to_tracks(pin_name, pin)
|
||||
pin_set.update(pin_in_tracks)
|
||||
pin_set.update(partial_in_tracks)
|
||||
|
||||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||
blockage_in_tracks = self.convert_blockage(pin)
|
||||
blockage_set.update(blockage_in_tracks)
|
||||
|
||||
debug.info(2," pins {}".format(pin_set))
|
||||
|
|
@ -534,12 +714,74 @@ class router:
|
|||
self.pin_components[pin_name].append(pin_set)
|
||||
|
||||
# Add all of the blocked grids to the set for the design
|
||||
self.blocked_grids.update(blockage_set)
|
||||
partial_set = blockage_set - pin_set
|
||||
self.pin_component_blockages[pin_name].append(partial_set)
|
||||
|
||||
# Remove the pins from the blockages since we didn't know
|
||||
# they were pins when processing blockages
|
||||
self.blocked_grids.difference_update(pin_set)
|
||||
|
||||
# Remove the blockage set from the blockages since these
|
||||
# will either be pins or partial pin blockges
|
||||
self.blocked_grids.difference_update(blockage_set)
|
||||
|
||||
def enclose_pin_grids(self, grids):
|
||||
"""
|
||||
This encloses a single pin component with a rectangle.
|
||||
It returns the set of the unenclosed pins.
|
||||
"""
|
||||
|
||||
# We may have started with an empty set
|
||||
if not grids:
|
||||
return
|
||||
|
||||
# Start with lowest left element
|
||||
ll = min(grids)
|
||||
grids.remove(ll)
|
||||
# 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 or blocked
|
||||
if right in grids and right not in self.blocked_grids:
|
||||
grids.remove(right)
|
||||
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 or blocked
|
||||
if cell not in grids or cell in self.blocked_grids:
|
||||
break
|
||||
else:
|
||||
grids.difference_update(set(next_row))
|
||||
row = next_row
|
||||
# Skips the second break
|
||||
continue
|
||||
# Breaks from the nested break
|
||||
break
|
||||
|
||||
# Add a shape from ll to ur
|
||||
ur = row[-1]
|
||||
self.add_enclosure(ll, ur, ll.z)
|
||||
|
||||
# Return the remaining grid set
|
||||
return grids
|
||||
|
||||
def enclose_pins(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.
|
||||
"""
|
||||
# FIXME: This could be optimized, but we just do a simple greedy biggest shape
|
||||
# for now.
|
||||
for pin_name in self.pin_components.keys():
|
||||
for pin_set,partial_set in zip(self.pin_components[pin_name],self.pin_component_blockages[pin_name]):
|
||||
total_pin_grids = pin_set | partial_set
|
||||
while self.enclose_pin_grids(total_pin_grids):
|
||||
pass
|
||||
|
||||
self.write_debug_gds("pin_debug.gds", False)
|
||||
|
||||
|
||||
def add_source(self, pin_name):
|
||||
|
|
@ -619,71 +861,6 @@ class router:
|
|||
self.set_blockages(component, value)
|
||||
|
||||
|
||||
def write_debug_gds(self):
|
||||
"""
|
||||
Write out a GDS file with the routing grid and search information annotated on it.
|
||||
"""
|
||||
# Only add the debug info to the gds file if we have any debugging on.
|
||||
# This is because we may reroute a wire with detours and don't want the debug information.
|
||||
if OPTS.debug_level==0: return
|
||||
|
||||
self.add_router_info()
|
||||
debug.error("Writing debug_route.gds")
|
||||
self.cell.gds_write("debug_route.gds")
|
||||
|
||||
def add_router_info(self):
|
||||
"""
|
||||
Write the routing grid and router cost, blockage, pins on
|
||||
the boundary layer for debugging purposes. This can only be
|
||||
called once or the labels will overlap.
|
||||
"""
|
||||
debug.info(0,"Adding router info")
|
||||
|
||||
if OPTS.debug_level>0:
|
||||
for blockage in self.blockages:
|
||||
# Display the inflated blockage
|
||||
(ll,ur) = blockage.inflate()
|
||||
self.cell.add_rect(layer="text",
|
||||
offset=ll,
|
||||
width=ur.x-ll.x,
|
||||
height=ur.y-ll.y)
|
||||
if OPTS.debug_level>1:
|
||||
grid_keys=self.rg.map.keys()
|
||||
partial_track=vector(0,self.track_width/6.0)
|
||||
for g in grid_keys:
|
||||
shape = self.convert_track_to_shape(g)
|
||||
self.cell.add_rect(layer="text",
|
||||
offset=shape[0],
|
||||
width=shape[1].x-shape[0].x,
|
||||
height=shape[1].y-shape[0].y)
|
||||
# These are the on grid pins
|
||||
#rect = self.convert_track_to_pin(g)
|
||||
#self.cell.add_rect(layer="boundary",
|
||||
# offset=rect[0],
|
||||
# width=rect[1].x-rect[0].x,
|
||||
# height=rect[1].y-rect[0].y)
|
||||
|
||||
t=self.rg.map[g].get_type()
|
||||
|
||||
# midpoint offset
|
||||
off=vector((shape[1].x+shape[0].x)/2,
|
||||
(shape[1].y+shape[0].y)/2)
|
||||
if g[2]==1:
|
||||
# Upper layer is upper right label
|
||||
type_off=off+partial_track
|
||||
else:
|
||||
# Lower layer is lower left label
|
||||
type_off=off-partial_track
|
||||
if t!=None:
|
||||
self.cell.add_label(text=str(t),
|
||||
layer="text",
|
||||
offset=type_off)
|
||||
self.cell.add_label(text="{0},{1}".format(g[0],g[1]),
|
||||
layer="text",
|
||||
offset=shape[0],
|
||||
zoom=0.05)
|
||||
|
||||
|
||||
def prepare_path(self,path):
|
||||
"""
|
||||
Prepare a path or wave for routing ebedding.
|
||||
|
|
@ -722,14 +899,14 @@ class router:
|
|||
|
||||
# If it is only a square, add an enclosure to the track
|
||||
if len(path)==1:
|
||||
self.add_enclosure(abs_path[0])
|
||||
self.add_single_enclosure(abs_path[0])
|
||||
else:
|
||||
# Otherwise, add the route which includes enclosures
|
||||
self.cell.add_route(layers=self.layers,
|
||||
coordinates=abs_path,
|
||||
layer_widths=self.layer_widths)
|
||||
|
||||
def add_enclosure(self, loc):
|
||||
def add_single_enclosure(self, loc):
|
||||
"""
|
||||
Add a metal enclosure that is the size of the routing grid minus a spacing on each side.
|
||||
"""
|
||||
|
|
@ -755,19 +932,45 @@ class router:
|
|||
def add_wavepath(self, name, path):
|
||||
"""
|
||||
Add the current wave to the given design instance.
|
||||
This is a single layer path that is multiple tracks wide.
|
||||
"""
|
||||
path=self.prepare_path(path)
|
||||
|
||||
# convert the path back to absolute units from tracks
|
||||
abs_path = [self.convert_wave_to_units(i) for i in path]
|
||||
ll = path[0][0]
|
||||
ur = path[-1][-1]
|
||||
z = ll.z
|
||||
|
||||
pin = self.add_enclosure(ll, ur, z, name)
|
||||
|
||||
return pin
|
||||
|
||||
ur = abs_path[-1][-1]
|
||||
ll = abs_path[0][0]
|
||||
pin = self.cell.add_layout_pin(name,
|
||||
layer=self.get_layer(path[0][0].z),
|
||||
offset=vector(ll.x,ll.y),
|
||||
width=ur.x-ll.x,
|
||||
height=ur.y-ll.y)
|
||||
def add_enclosure(self, ll, ur, zindex, name=""):
|
||||
"""
|
||||
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.
|
||||
|
||||
"""
|
||||
# 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
|
||||
(abs_ll,unused) = self.convert_track_to_pin(ll)
|
||||
(unused,abs_ur) = self.convert_track_to_pin(ur)
|
||||
#print("enclose ll={0} ur={1}".format(ll,ur))
|
||||
#print("enclose ll={0} ur={1}".format(abs_ll,abs_ur))
|
||||
|
||||
if name:
|
||||
pin = self.cell.add_layout_pin(name,
|
||||
layer=layer,
|
||||
offset=abs_ll,
|
||||
width=abs_ur.x-abs_ll.x,
|
||||
height=abs_ur.y-abs_ll.y)
|
||||
else:
|
||||
pin = self.cell.add_rect(layer=layer,
|
||||
offset=abs_ll,
|
||||
width=abs_ur.x-abs_ll.x,
|
||||
height=abs_ur.y-abs_ll.y)
|
||||
return pin
|
||||
|
||||
|
||||
|
|
@ -832,20 +1035,105 @@ class router:
|
|||
self.reinit()
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def annotate_pin_and_tracks(self, pin, tracks):
|
||||
""""
|
||||
Annotate some shapes for debug purposes
|
||||
"""
|
||||
debug.info(0,"Annotating\n pin {0}\n tracks {1}".format(pin,tracks))
|
||||
for coord in tracks:
|
||||
(ll,ur) = self.convert_track_to_shape(coord)
|
||||
self.cell.add_rect(layer="text",
|
||||
offset=ll,
|
||||
width=ur[0]-ll[0],
|
||||
height=ur[1]-ll[1])
|
||||
(ll,ur) = self.convert_track_to_pin(coord)
|
||||
self.cell.add_rect(layer="boundary",
|
||||
offset=ll,
|
||||
width=ur[0]-ll[0],
|
||||
height=ur[1]-ll[1])
|
||||
(ll,ur) = pin.rect
|
||||
self.cell.add_rect(layer="text",
|
||||
offset=ll,
|
||||
width=ur[0]-ll[0],
|
||||
height=ur[1]-ll[1])
|
||||
|
||||
def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True):
|
||||
"""
|
||||
Write out a GDS file with the routing grid and search information annotated on it.
|
||||
"""
|
||||
# Only add the debug info to the gds file if we have any debugging on.
|
||||
# This is because we may reroute a wire with detours and don't want the debug information.
|
||||
if OPTS.debug_level==0: return
|
||||
|
||||
self.add_router_info()
|
||||
self.cell.gds_write(gds_name)
|
||||
|
||||
if stop_program:
|
||||
import sys
|
||||
sys.exit(1)
|
||||
|
||||
def add_router_info(self):
|
||||
"""
|
||||
Write the routing grid and router cost, blockage, pins on
|
||||
the boundary layer for debugging purposes. This can only be
|
||||
called once or the labels will overlap.
|
||||
"""
|
||||
debug.info(0,"Adding router info")
|
||||
|
||||
if OPTS.debug_level==0:
|
||||
# Display the inflated blockage
|
||||
for blockage in self.blockages:
|
||||
debug.info(1,"Adding {}".format(blockage))
|
||||
(ll,ur) = blockage.inflate()
|
||||
self.cell.add_rect(layer="text",
|
||||
offset=ll,
|
||||
width=ur.x-ll.x,
|
||||
height=ur.y-ll.y)
|
||||
if OPTS.debug_level>1:
|
||||
#self.set_blockages(self.blocked_grids,True)
|
||||
grid_keys=self.rg.map.keys()
|
||||
partial_track=vector(0,self.track_width/6.0)
|
||||
for g in grid_keys:
|
||||
shape = self.convert_track_to_shape(g)
|
||||
self.cell.add_rect(layer="text",
|
||||
offset=shape[0],
|
||||
width=shape[1].x-shape[0].x,
|
||||
height=shape[1].y-shape[0].y)
|
||||
t=self.rg.map[g].get_type()
|
||||
|
||||
# midpoint offset
|
||||
off=vector((shape[1].x+shape[0].x)/2,
|
||||
(shape[1].y+shape[0].y)/2)
|
||||
if g[2]==1:
|
||||
# Upper layer is upper right label
|
||||
type_off=off+partial_track
|
||||
else:
|
||||
# Lower layer is lower left label
|
||||
type_off=off-partial_track
|
||||
if t!=None:
|
||||
self.cell.add_label(text=str(t),
|
||||
layer="text",
|
||||
offset=type_off)
|
||||
self.cell.add_label(text="{0},{1}".format(g[0],g[1]),
|
||||
layer="text",
|
||||
offset=shape[0],
|
||||
zoom=0.05)
|
||||
|
||||
|
||||
# FIXME: This should be replaced with vector.snap_to_grid at some point
|
||||
|
||||
def snap_to_grid(offset):
|
||||
"""
|
||||
Changes the coodrinate to match the grid settings
|
||||
"""
|
||||
grid = tech.drc["grid"]
|
||||
x = offset[0]
|
||||
y = offset[1]
|
||||
# this gets the nearest integer value
|
||||
xgrid = int(round(round((x / grid), 2), 0))
|
||||
ygrid = int(round(round((y / grid), 2), 0))
|
||||
xoff = xgrid * grid
|
||||
yoff = ygrid * grid
|
||||
xoff = snap_val_to_grid(offset[0])
|
||||
yoff = snap_val_to_grid(offset[1])
|
||||
return vector(xoff, yoff)
|
||||
|
||||
def snap_val_to_grid(x):
|
||||
grid = tech.drc["grid"]
|
||||
xgrid = int(round(round((x / grid), 2), 0))
|
||||
xoff = xgrid * grid
|
||||
return xoff
|
||||
|
|
|
|||
|
|
@ -58,13 +58,14 @@ class signal_router(router):
|
|||
# FIXME: This could be created only over the routing region,
|
||||
# but this is simplest for now.
|
||||
self.create_routing_grid()
|
||||
# This will get all shapes as blockages
|
||||
self.find_blockages()
|
||||
|
||||
# Now add the blockages (all shapes except the pins)
|
||||
self.find_pins(src)
|
||||
self.find_pins(dest)
|
||||
|
||||
# This will get all shapes as blockages
|
||||
self.find_blockages()
|
||||
|
||||
# Now add the blockages
|
||||
self.set_blockages(self.blocked_grids,True)
|
||||
#self.set_blockages(self.pin_partials[src],True)
|
||||
|
|
|
|||
|
|
@ -57,13 +57,10 @@ class supply_router(router):
|
|||
# FIXME: This could be created only over the routing region,
|
||||
# but this is simplest for now.
|
||||
self.create_routing_grid()
|
||||
# This will get all shapes as blockages
|
||||
self.find_blockages()
|
||||
|
||||
# Get the pin shapes
|
||||
self.find_pins(self.vdd_name)
|
||||
self.find_pins(self.gnd_name)
|
||||
|
||||
self.find_pins_and_blockages()
|
||||
|
||||
# Add the supply rails in a mesh network and connect H/V with vias
|
||||
# Block everything
|
||||
self.prepare_blockages()
|
||||
|
|
@ -84,9 +81,30 @@ class supply_router(router):
|
|||
self.route_pins_to_rails(gnd_name)
|
||||
self.route_pins_to_rails(vdd_name)
|
||||
|
||||
self.write_debug_gds()
|
||||
self.write_debug_gds(stop_program=False)
|
||||
return True
|
||||
|
||||
def find_pins_and_blockages(self):
|
||||
"""
|
||||
Find the pins and blockages in teh design
|
||||
"""
|
||||
# This finds the pin shapes and sorts them into "groups" that are connected
|
||||
self.find_pins(self.vdd_name)
|
||||
self.find_pins(self.gnd_name)
|
||||
|
||||
# This will get all shapes as blockages and convert to grid units
|
||||
# This ignores shapes that were pins
|
||||
self.find_blockages()
|
||||
|
||||
# This will convert the pins to grid units
|
||||
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids
|
||||
self.convert_pins(self.vdd_name)
|
||||
self.convert_pins(self.gnd_name)
|
||||
|
||||
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
|
||||
self.enclose_pins()
|
||||
|
||||
|
||||
def prepare_blockages(self):
|
||||
"""
|
||||
Reset and add all of the blockages in the design.
|
||||
|
|
@ -104,7 +122,11 @@ class supply_router(router):
|
|||
# Block all of the pin components (some will be unblocked if they're a source/target)
|
||||
for name in self.pin_components.keys():
|
||||
self.set_blockages(self.pin_components[name],True)
|
||||
|
||||
|
||||
# Block all of the pin component partial blockages
|
||||
for name in self.pin_component_blockages.keys():
|
||||
self.set_blockages(self.pin_component_blockages[name],True)
|
||||
|
||||
# These are the paths that have already been routed.
|
||||
self.set_path_blockages()
|
||||
|
||||
|
|
@ -234,11 +256,11 @@ class supply_router(router):
|
|||
|
||||
|
||||
num_components = self.num_pin_components(pin_name)
|
||||
debug.info(0,"Pin {0} has {1} components to route.".format(pin_name, num_components))
|
||||
debug.info(1,"Pin {0} has {1} components to route.".format(pin_name, num_components))
|
||||
|
||||
# For every component
|
||||
for index in range(num_components):
|
||||
debug.info(0,"Routing component {0} {1}".format(pin_name, index))
|
||||
debug.info(2,"Routing component {0} {1}".format(pin_name, index))
|
||||
|
||||
self.rg.reinit()
|
||||
|
||||
|
|
@ -253,12 +275,8 @@ class supply_router(router):
|
|||
self.add_supply_rail_target(pin_name)
|
||||
|
||||
# Actually run the A* router
|
||||
self.run_router(detour_scale=5)
|
||||
|
||||
#if index==1:
|
||||
# self.write_debug_gds()
|
||||
# import sys
|
||||
# sys.exit(1)
|
||||
if not self.run_router(detour_scale=5):
|
||||
self.write_debug_gds()
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,15 @@ class vector3d():
|
|||
return self.x==other.x and self.y==other.y and self.z==other.z
|
||||
return False
|
||||
|
||||
def __lt__(self, other):
|
||||
"""Override the default less than behavior"""
|
||||
if isinstance(other, self.__class__):
|
||||
if self.x<other.x:
|
||||
return True
|
||||
if self.x==other.x and self.y<other.y:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
"""Override the default non-equality behavior"""
|
||||
return not self.__eq__(other)
|
||||
|
|
|
|||
Loading…
Reference in New Issue