Supply router working except for off by one rail via error

This commit is contained in:
Matt Guthaus 2018-10-19 14:21:03 -07:00
parent 4bf1e206e2
commit 0aad61892b
4 changed files with 315 additions and 87 deletions

View File

@ -134,11 +134,13 @@ class layout(lef.lef):
return inst
return None
def add_rect(self, layer, offset, width=0, height=0):
"""Adds a rectangle on a given layer,offset with width and height"""
if width==0:
def add_rect(self, layer, offset, width=None, height=None):
"""
Adds a rectangle on a given layer,offset with width and height
"""
if not width:
width=drc["minwidth_{}".format(layer)]
if height==0:
if not height:
height=drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
layer_num = techlayer[layer]
@ -147,11 +149,13 @@ class layout(lef.lef):
return self.objs[-1]
return None
def add_rect_center(self, layer, offset, width=0, height=0):
"""Adds a rectangle on a given layer at the center point with width and height"""
if width==0:
def add_rect_center(self, layer, offset, width=None, height=None):
"""
Adds a rectangle on a given layer at the center point with width and height
"""
if not width:
width=drc["minwidth_{}".format(layer)]
if height==0:
if not height:
height=drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
layer_num = techlayer[layer]
@ -163,7 +167,9 @@ class layout(lef.lef):
def add_segment_center(self, layer, start, end):
""" Add a min-width rectanglular segment using center line on the start to end point """
"""
Add a min-width rectanglular segment using center line on the start to end point
"""
minwidth_layer = drc["minwidth_{}".format(layer)]
if start.x!=end.x and start.y!=end.y:
debug.error("Nonrectilinear center rect!",-1)
@ -177,7 +183,9 @@ class layout(lef.lef):
def get_pin(self, text):
""" Return the pin or list of pins """
"""
Return the pin or list of pins
"""
try:
if len(self.pin_map[text])>1:
debug.error("Should use a pin iterator since more than one pin {}".format(text),-1)
@ -192,7 +200,9 @@ class layout(lef.lef):
def get_pins(self, text):
""" Return a pin list (instead of a single pin) """
"""
Return a pin list (instead of a single pin)
"""
if text in self.pin_map.keys():
return self.pin_map[text]
else:
@ -210,7 +220,9 @@ class layout(lef.lef):
self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height())
def add_layout_pin_segment_center(self, text, layer, start, end):
""" Creates a path like pin with center-line convention """
"""
Creates a path like pin with center-line convention
"""
debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.")
@ -235,9 +247,9 @@ class layout(lef.lef):
def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None):
""" Creates a path like pin with center-line convention """
if width==None:
if not width:
width=drc["minwidth_{0}".format(layer)]
if height==None:
if not height:
height=drc["minwidth_{0}".format(layer)]
ll_offset = offset - vector(0.5*width,0.5*height)
@ -246,14 +258,18 @@ class layout(lef.lef):
def remove_layout_pin(self, text):
"""Delete a labeled pin (or all pins of the same name)"""
"""
Delete a labeled pin (or all pins of the same name)
"""
self.pin_map[text]=[]
def add_layout_pin(self, text, layer, offset, width=None, height=None):
"""Create a labeled pin """
if width==None:
"""
Create a labeled pin
"""
if not width:
width=drc["minwidth_{0}".format(layer)]
if height==None:
if not height:
height=drc["minwidth_{0}".format(layer)]
new_pin = pin_layout(text, [offset,offset+vector(width,height)], layer)
@ -273,13 +289,14 @@ class layout(lef.lef):
return new_pin
def add_label_pin(self, text, layer, offset, width=None, height=None):
"""Create a labeled pin WITHOUT the pin data structure. This is not an
"""
Create a labeled pin WITHOUT the pin data structure. This is not an
actual pin but a named net so that we can add a correspondence point
in LVS.
"""
if width==None:
if not width:
width=drc["minwidth_{0}".format(layer)]
if height==None:
if not height:
height=drc["minwidth_{0}".format(layer)]
self.add_rect(layer=layer,
offset=offset,

View File

@ -36,6 +36,12 @@ class grid:
# let's leave the map sparse, cells are created on demand to reduce memory
self.map={}
def add_all_grids(self):
for x in range(self.ll.x, self.ur.x, 1):
for y in range(self.ll.y, self.ur.y, 1):
self.add_map(vector3d(x,y,0))
self.add_map(vector3d(x,y,1))
def set_blocked(self,n,value=True):
if isinstance(n, (list,tuple,set,frozenset)):
for item in n:

View File

@ -62,8 +62,6 @@ class router:
### The routed data structures
# A list of paths that have been "routed"
self.paths = []
# The list of supply rails that may be routed
self.supply_rails = []
# The boundary will determine the limits to the size of the routing grid
self.boundary = self.layout.measureBoundary(self.top_name)
@ -833,8 +831,31 @@ class router:
# Add a shape from ll to ur
ur = row[-1]
return self.add_enclosure(ll, ur, ll.z)
return self.compute_pin_enclosure(ll, ur, ll.z)
def remove_redundant_shapes(self, pin_list):
"""
Remove any pin layout that is contained within another.
"""
print("INITIAL:",pin_list)
# Make a copy of the list to start
new_pin_list = pin_list.copy()
# This is n^2, but the number is small
for pin1 in pin_list:
for pin2 in pin_list:
# Can't contain yourself
if pin1 == pin2:
continue
if pin2.contains(pin1):
# It may have already been removed by being enclosed in another pin
if pin1 in new_pin_list:
new_pin_list.remove(pin1)
print("FINAL :",new_pin_list)
return new_pin_list
def compute_enclosures(self, tracks):
"""
@ -844,19 +865,7 @@ class router:
for seed in tracks:
pin_list.append(self.enclose_pin_grids(tracks, seed))
# Prune any enclosre that is contained in another
new_pin_list = pin_list
for pin1 in pin_list:
for pin2 in pin_list:
if pin1 == pin2:
continue
if pin2.contains(pin1):
try:
new_pin_list.remove(pin1)
except ValueError:
pass
return new_pin_list
return self.remove_redundant_shapes(pin_list)
def overlap_any_shape(self, pin_list, shape_list):
"""
@ -900,19 +909,24 @@ class router:
put a rectangle over it. It does not enclose grid squares that are blocked
by other shapes.
"""
# These are used for debugging
self.connector_enclosure = []
self.enclosures = []
for pin_name in self.pin_grids.keys():
debug.info(1,"Enclosing pins for {}".format(pin_name))
debug.check(len(self.pin_groups[pin_name])==len(self.pin_grids[pin_name]),"Unequal pin_group and pin_grid")
for pin_group,pin_set in zip(self.pin_groups[pin_name],self.pin_grids[pin_name]):
for pin_group,pin_grid_set in zip(self.pin_groups[pin_name],self.pin_grids[pin_name]):
# Compute the enclosure pin_layout list of the set of tracks
enclosure_list = self.compute_enclosures(pin_set)
enclosure_list = self.compute_enclosures(pin_grid_set)
for pin in enclosure_list:
debug.info(2,"Adding enclosure {0} {1}".format(pin_name, pin))
self.cell.add_rect(layer=pin.layer,
offset=pin.ll(),
width=pin.width(),
height=pin.height())
self.enclosures.append(pin)
# Check if a pin shape overlaps any enclosure.
# If so, we are done.
@ -926,6 +940,7 @@ class router:
offset=new_enclosure.ll(),
width=new_enclosure.width(),
height=new_enclosure.height())
self.connector_enclosure.append(new_enclosure)
@ -956,7 +971,7 @@ class router:
xmin = min(pbc.x,ebc.x)
xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y)
ur = vetor(xmax, puc.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
@ -1112,7 +1127,7 @@ class router:
ll = path[0][0]
ur = path[-1][-1]
z = ll.z
pin = self.add_enclosure(ll, ur, z, name)
pin = self.compute_wide_enclosure(ll, ur, z, name)
#print(ll, ur, ll.z, "->",pin)
self.cell.add_layout_pin(text=name,
layer=pin.layer,
@ -1122,7 +1137,28 @@ class router:
return pin
def add_enclosure(self, ll, ur, zindex, name=""):
def compute_pin_enclosure(self, ll, ur, zindex, name=""):
"""
Enclose the tracks from ll to ur in a single rectangle that meets
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))
pin = pin_layout(name, [abs_ll, abs_ur], layer)
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.
If name is supplied, it is added as a pin and not just a rectangle.
@ -1142,14 +1178,18 @@ class router:
# 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)
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 + vector(0.5*space, 0.5*space)
new_ur = abs_ur - vector(0.5*space, 0.5*space)
new_ll = abs_ll + spacing
new_ur = abs_ur - spacing
pin = pin_layout(name, [new_ll, new_ur], layer)
return pin
def get_inertia(self,p0,p1):
"""
@ -1246,7 +1286,37 @@ class router:
if stop_program:
import sys
sys.exit(1)
def annotate_grid(self, g):
"""
Display grid information in the GDS file for a single grid cell.
"""
shape = self.convert_track_to_shape(g)
partial_track=vector(0,self.track_width/6.0)
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)
def add_router_info(self):
"""
Write the routing grid and router cost, blockage, pins on
@ -1255,7 +1325,18 @@ class router:
"""
debug.info(0,"Adding router info")
if OPTS.debug_level>0:
show_blockages = False
show_blockage_grids = False
show_enclosures = False
show_connectors = False
show_all_grids = True
if show_all_grids:
self.rg.add_all_grids()
for g in self.rg.map.keys():
self.annotate_grid(g)
if show_blockages:
# Display the inflated blockage
for blockage in self.blockages:
debug.info(1,"Adding {}".format(blockage))
@ -1264,36 +1345,26 @@ class router:
offset=ll,
width=ur.x-ll.x,
height=ur.y-ll.y)
if OPTS.debug_level>1:
if show_blockage_grids:
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)
self.annotate_grid(g)
if show_connectors:
for pin in self.connector_enclosure:
#print("connector: ",str(pin))
self.cell.add_rect(layer="text",
offset=pin.ll(),
width=pin.width(),
height=pin.height())
if show_enclosures:
for pin in self.enclosures:
#print("enclosure: ",pin.name,pin.ll(),pin.width(),pin.height())
self.cell.add_rect(layer="text",
offset=pin.ll(),
width=pin.width(),
height=pin.height())
# FIXME: This should be replaced with vector.snap_to_grid at some point

View File

@ -4,7 +4,6 @@ from contact import contact
import math
import debug
from globals import OPTS
import grid
from pin_layout import pin_layout
from vector import vector
from vector3d import vector3d
@ -24,6 +23,13 @@ class supply_router(router):
"""
router.__init__(self, layers, design, gds_filename)
# The list of supply rails that may be routed
self.supply_rails = []
# This is the same as above but the sets of pins
self.supply_rail_tracks = {}
self.supply_rail_wire_tracks = {}
# Power rail width in grid units.
self.rail_track_width = 2
@ -57,7 +63,9 @@ class supply_router(router):
# Get the pin shapes
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
#self.write_debug_gds("pin_enclosures.gds",stop_program=True)
# Add the supply rails in a mesh network and connect H/V with vias
# Block everything
self.prepare_blockages(self.gnd_name)
@ -70,17 +78,108 @@ class supply_router(router):
self.route_supply_rails(self.vdd_name,1)
#self.write_debug_gds("pre_pin_debug.gds",stop_program=True)
remaining_vdd_pin_indices = self.route_simple_overlaps(vdd_name)
remaining_gnd_pin_indices = self.route_simple_overlaps(gnd_name)
# Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter
self.route_pins_to_rails(vdd_name)
self.route_pins_to_rails(gnd_name)
self.route_pins_to_rails(vdd_name, remaining_vdd_pin_indices)
self.route_pins_to_rails(gnd_name, remaining_gnd_pin_indices)
#self.write_debug_gds("post_pin_debug.gds",stop_program=False)
self.write_debug_gds("post_pin_debug.gds",stop_program=False)
return True
def route_simple_overlaps(self, pin_name):
"""
This checks for simple cases where a pin component already overlaps a supply rail.
It will add an enclosure to ensure the overlap in wide DRC rule cases.
"""
num_components = self.num_pin_components(pin_name)
remaining_pins = []
supply_tracks = self.supply_rail_tracks[pin_name]
for index in range(num_components):
pin_in_tracks = self.pin_grids[pin_name][index]
common_set = supply_tracks & pin_in_tracks
if len(common_set)==0:
# if no overlap, add it to the complex route pins
remaining_pins.append(index)
else:
print("Overlap!",index)
self.create_simple_overlap_enclosure(pin_name, common_set)
return remaining_pins
def recurse_simple_overlap_enclosure(self, pin_name, 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.
"""
import grid_utils
next_set = grid_utils.expand_border(start_set, direct)
supply_tracks = self.supply_rail_tracks[pin_name]
supply_wire_tracks = self.supply_rail_wire_tracks[pin_name]
supply_overlap = next_set & supply_tracks
wire_overlap = next_set & supply_wire_tracks
print("EXAMINING: ",start_set,len(start_set),len(supply_overlap),len(wire_overlap),direct)
# If the rail overlap is the same, we are done, since we connected to the actual wire
if len(wire_overlap)==len(start_set):
print("HIT RAIL", 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
elif len(supply_overlap)==len(start_set):
print("RECURSE", supply_overlap)
recurse_set = self.recurse_simple_overlap_enclosure(pin_name, supply_overlap, direct)
new_set = start_set | supply_overlap | recurse_set
else:
# If we got no next set, we are done, can't expand!
print("NO MORE OVERLAP", supply_overlap)
new_set = set()
return new_set
def create_simple_overlap_enclosure(self, pin_name, 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.
"""
import grid_utils
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(pin_name, start_set, direction.NORTH)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.SOUTH)
else:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.EAST)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.WEST)
enclosure_list = self.compute_enclosures(new_set)
for pin in enclosure_list:
debug.info(2,"Adding simple overlap enclosure {0} {1}".format(pin_name, pin))
self.cell.add_rect(layer=pin.layer,
offset=pin.ll(),
width=pin.width(),
height=pin.height())
def connect_supply_rails(self, name):
"""
Determine which supply rails overlap and can accomodate a via.
@ -119,7 +218,7 @@ class supply_router(router):
remove_hrails = [rail for flag,rail in zip(horizontal_flags,horizontal_rails) if not flag]
remove_vrails = [rail for flag,rail in zip(vertical_flags,vertical_rails) if not flag]
for rail in remove_hrails + remove_vrails:
debug.info(1,"Removing disconnected supply rail {}".format(rail))
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(rail[0][0],rail[-1][-1]))
self.supply_rails.remove(rail)
def add_supply_rails(self, name):
@ -155,9 +254,19 @@ class supply_router(router):
# 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
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
debug.check(total_space % 2 == 0, "Asymmetric wire track spacing...")
self.supply_rail_space_width = int(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):
@ -244,21 +353,46 @@ class supply_router(router):
# Add the rails themselves
self.add_supply_rails(name)
# Make the supply rails into a big giant set of grids
self.create_supply_track_set(name)
def create_supply_track_set(self, pin_name):
"""
Take the remaining supply rails and put the middle grids into a set.
The middle grids will be guaranteed to have the wire.
FIXME: Do this instead with the supply_rail_pitch and width
"""
rail_set = set()
wire_set = set()
for rail in self.supply_rails:
if rail.name != pin_name:
continue
# FIXME: Select based on self.supply_rail_wire_width and self.supply_rail_width
start_wire_index = self.supply_rail_space_width
end_wire_index = self.supply_rail_width - self.supply_rail_space_width
for wave_index in range(len(rail)):
rail_set.update(rail[wave_index])
wire_set.update(rail[wave_index][start_wire_index:end_wire_index])
self.supply_rail_tracks[pin_name] = rail_set
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, remaining_component_indices):
"""
This will route each of the pin components to the supply rails.
This will route each of the remaining pin components to the supply rails.
After it is done, the cells are added to the pin blockage list.
"""
num_components = self.num_pin_components(pin_name)
debug.info(1,"Pin {0} has {1} components to route.".format(pin_name, num_components))
debug.info(1,"Pin {0} has {1} remaining components to route.".format(pin_name,
len(remaining_component_indices)))
recent_paths = []
# For every component
for index in range(num_components):
for index in remaining_component_indices:
debug.info(2,"Routing component {0} {1}".format(pin_name, index))
self.rg.reinit()