Updates to supply routing.

Rename astar_grid to signal_grid to parallel supply routing.
Wave expansion for supply rails.
Pin addition for supply rails.
This commit is contained in:
Matt Guthaus 2018-09-06 11:54:14 -07:00
parent 59956f1446
commit cd987479b8
8 changed files with 294 additions and 109 deletions

View File

@ -15,12 +15,12 @@ class vector():
""" init function support two init method"""
# will take single input as a coordinate
if y==None:
self.x = x[0]
self.y = x[1]
self.x = float(x[0])
self.y = float(x[1])
#will take two inputs as the values of a coordinate
else:
self.x = x
self.y = y
self.x = float(x)
self.y = float(y)
def __str__(self):
""" override print function output """
@ -36,12 +36,12 @@ class vector():
can set value by vector[index]=value
"""
if index==0:
self.x=value
self.x=float(value)
elif index==1:
self.y=value
self.y=float(value)
else:
self.x=value[0]
self.y=value[1]
self.x=float(value[0])
self.y=float(value[1])
def __getitem__(self, index):
"""
@ -84,6 +84,14 @@ class vector():
"""
return vector(other[0]- self.x, other[1] - self.y)
def __hash__(self):
"""
Override - function (hash)
Note: This assumes that you DON'T CHANGE THE VECTOR or it will
break things.
"""
return hash((self.x,self.y))
def snap_to_grid(self):
self.x = self.snap_offset_to_grid(self.x)
self.y = self.snap_offset_to_grid(self.y)

View File

@ -12,7 +12,7 @@ class grid:
or horizontal layer.
"""
def __init__(self):
def __init__(self, ll, ur, track_width):
""" Initialize the map and define the costs. """
# costs are relative to a unit grid
@ -22,17 +22,43 @@ class grid:
self.NONPREFERRED_COST = 4
self.PREFERRED_COST = 1
# list of the source/target grid coordinates
self.source = []
self.target = []
self.track_width = track_width
self.track_widths = [self.track_width, self.track_width, 1.0]
self.track_factor = [1/self.track_width, 1/self.track_width, 1.0]
# The bounds are in grids for this
# This is really lower left bottom layer and upper right top layer in 3D.
self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round()
self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round()
# let's leave the map sparse, cells are created on demand to reduce memory
self.map={}
def set_blocked(self,n):
self.add_map(n)
self.map[n].blocked=True
def set_blocked(self,n,value=True):
if isinstance(n,list):
for item in n:
self.set_blocked(item,value)
else:
self.add_map(n)
self.map[n].blocked=value
def is_blocked(self,n):
self.add_map(n)
return self.map[n].blocked
def set_path(self,n,value=True):
if isinstance(n,list):
for item in n:
self.set_path(item,value)
else:
self.add_map(n)
self.map[n].path=value
def add_blockage_shape(self,ll,ur,z):
debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
@ -48,12 +74,54 @@ class grid:
for n in block_list:
self.set_blocked(n)
def add_map(self,p):
def set_source(self,n):
if isinstance(n,list):
for item in n:
self.set_source(item)
else:
self.add_map(n)
self.map[n].source=True
self.source.append(n)
def set_target(self,n):
if isinstance(n,list):
for item in n:
self.set_target(item)
else:
self.add_map(n)
self.map[n].target=True
self.target.append(n)
def add_source(self,track_list):
debug.info(2,"Adding source list={0}".format(str(track_list)))
for n in track_list:
debug.info(3,"Adding source ={0}".format(str(n)))
self.set_source(n)
def add_target(self,track_list):
debug.info(2,"Adding target list={0}".format(str(track_list)))
for n in track_list:
debug.info(3,"Adding target ={0}".format(str(n)))
self.set_target(n)
def is_target(self,point):
"""
Point is in the target set, so we are done.
"""
return point in self.target
def add_map(self,n):
"""
Add a point to the map if it doesn't exist.
"""
if p not in self.map.keys():
self.map[p]=cell()
if isinstance(n,list):
for item in n:
self.add_map(item)
else:
if n not in self.map.keys():
self.map[n]=cell()
def add_path(self,path):
"""
@ -61,7 +129,7 @@ class grid:
"""
self.path=path
for p in path:
self.map[p].path=True
self.set_path(p)
def block_path(self,path):
"""
@ -69,8 +137,8 @@ class grid:
Also unsets the path flag.
"""
for p in path:
self.map[p].path=False
self.map[p].blocked=True
self.set_path(p,False)
self.set_blocked(p)
def cost(self,path):
"""

View File

@ -35,16 +35,31 @@ class router:
self.reader.loadFromFile(gds_name)
self.top_name = self.layout.rootStructureName
# A map of pin names to pin structures
self.pins = {}
# A list of pin blockages (represented by the pin structures too)
self.blockages=[]
# all the paths we've routed so far (to supplement the blockages)
self.paths = []
self.wave_paths = []
# The boundary will determine the limits to the size of the routing grid
self.boundary = self.layout.measureBoundary(self.top_name)
self.ll = vector(self.boundary[0])
self.ur = vector(self.boundary[1])
# These must be un-indexed to get rid of the matrix type
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
def clear_pins(self):
"""
Convert the routed path to blockages.
Keep the other blockages unchanged.
"""
self.pins = {}
self.rg.reinit()
def set_top(self,top_name):
""" If we want to route something besides the top-level cell."""
self.top_name = top_name
@ -55,6 +70,20 @@ class router:
else:
return 1
def get_layer(self, zindex):
if zindex==1:
return self.vert_layer_name
elif zindex==0:
return self.horiz_layer_name
else:
debug.error(-1,"Invalid zindex {}".format(zindex))
def is_wave(self,path):
"""
Determines if this is a wave (True) or a normal route (False)
"""
return isinstance(path[0],list)
def set_layers(self, layers):
"""Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always
@ -254,9 +283,16 @@ class router:
Convert a path set of tracks to center line path.
"""
pt = vector3d(p)
pt=pt.scale(self.track_widths[0],self.track_widths[1],1)
pt = pt.scale(self.track_widths[0],self.track_widths[1],1)
return pt
def convert_wave_to_units(self,wave):
"""
Convert a wave to a set of center points
"""
return [self.convert_point_to_units(i) for i in wave]
def convert_blockage_to_tracks(self,shape):
"""
Convert a rectangular blockage shape into track units.
@ -305,8 +341,8 @@ class router:
track_list = []
block_list = []
for x in range(ll[0],ur[0]):
for y in range(ll[1],ur[1]):
for x in range(int(ll[0]),int(ur[0])):
for y in range(int(ll[1]),int(ur[1])):
debug.info(1,"Converting [ {0} , {1} ]".format(x,y))
# however, if there is not enough overlap, then if there is any overlap at all,
@ -481,9 +517,9 @@ class router:
height=ur.y-ll.y)
def add_route(self,path):
def prepare_path(self,path):
"""
Add the current wire route to the given design instance.
Prepare a path or wave for routing
"""
debug.info(3,"Set path: " + str(path))
@ -497,18 +533,46 @@ class router:
if False or path==None:
self.write_debug_gds()
# First, simplify the path for
#debug.info(1,str(self.path))
contracted_path = self.contract_path(path)
debug.info(1,str(contracted_path))
return contracted_path
def add_route(self,path):
"""
Add the current wire route to the given design instance.
"""
path=self.prepare_path(path)
# convert the path back to absolute units from tracks
abs_path = map(self.convert_point_to_units,contracted_path)
abs_path = list(map(self.convert_point_to_units,path))
debug.info(1,str(abs_path))
self.cell.add_route(self.layers,abs_path)
def add_wave(self, name, path):
"""
Add the current wave to the given design instance.
"""
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]
debug.info(1,str(abs_path))
if self.is_wave(path):
ur = abs_path[-1][-1]
ll = abs_path[0][0]
self.cell.add_layout_pin(name,
layer=self.get_layer(ll.z),
offset=vector(ll.x,ll.y),
width=ur.x-ll.x,
height=ur.y-ll.y)
def get_inertia(self,p0,p1):
"""
Sets the direction based on the previous direction we came from.
@ -524,8 +588,13 @@ class router:
def contract_path(self,path):
"""
Remove intermediate points in a rectilinear path.
Remove intermediate points in a rectilinear path or a wave.
"""
# Waves are always linear, so just return the first and last.
if self.is_wave(path):
return [path[0],path[-1]]
# Make a list only of points that change inertia of the path
newpath = [path[0]]
for i in range(1,len(path)-1):
prev_inertia=self.get_inertia(path[i-1],path[i])

View File

@ -4,52 +4,18 @@ from vector3d import vector3d
import grid
from heapq import heappush,heappop
class astar_grid(grid.grid):
class signal_grid(grid.grid):
"""
Expand the two layer grid to include A* search functions for a source and target.
"""
def __init__(self):
def __init__(self, ll, ur, track_factor):
""" Create a routing map of width x height cells and 2 in the z-axis. """
grid.grid.__init__(self)
grid.grid.__init__(self, ll, ur, track_factor)
# list of the source/target grid coordinates
self.source = []
self.target = []
# priority queue for the maze routing
self.q = []
def set_source(self,n):
self.add_map(n)
self.map[n].source=True
self.source.append(n)
def set_target(self,n):
self.add_map(n)
self.map[n].target=True
self.target.append(n)
def add_source(self,track_list):
debug.info(2,"Adding source list={0}".format(str(track_list)))
for n in track_list:
debug.info(3,"Adding source ={0}".format(str(n)))
self.set_source(n)
def add_target(self,track_list):
debug.info(2,"Adding target list={0}".format(str(track_list)))
for n in track_list:
debug.info(3,"Adding target ={0}".format(str(n)))
self.set_target(n)
def is_target(self,point):
"""
Point is in the target set, so we are done.
"""
return point in self.target
def reinit(self):
""" Reinitialize everything for a new route. """

View File

@ -10,21 +10,18 @@ from globals import OPTS
from router import router
class signal_router(router):
"""A router class to read an obstruction map from a gds and plan a
"""
A router class to read an obstruction map from a gds and plan a
route on a given layer. This is limited to two layer routes.
"""
def __init__(self, gds_name=None, module=None):
"""Use the gds file for the blockages with the top module topName and
"""
Use the gds file for the blockages with the top module topName and
layers for the layers to route on
"""
router.__init__(self, gds_name, module)
self.pins = {}
# all the paths we've routed so far (to supplement the blockages)
self.paths = []
def create_routing_grid(self):
"""
@ -35,8 +32,8 @@ class signal_router(router):
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
import astar_grid
self.rg = astar_grid.astar_grid()
import signal_grid
self.rg = signal_grid.signal_grid(self.ll, self.ur, self.track_width)
def route(self, cell, layers, src, dest, detour_scale=5):

View File

@ -9,13 +9,12 @@ class supply_grid(grid.grid):
or horizontal layer.
"""
def __init__(self):
def __init__(self, ll, ur, track_width):
""" Create a routing map of width x height cells and 2 in the z-axis. """
grid.grid.__init__(self)
grid.grid.__init__(self, ll, ur, track_width)
# list of the vdd/gnd rail cells
self.vdd_rails = []
self.gnd_rails = []
# Current rail
self.rail = []
def reinit(self):
""" Reinitialize everything for a new route. """
@ -25,3 +24,63 @@ class supply_grid(grid.grid):
p.reset()
def start_wave(self, loc, width):
"""
Finds the first loc starting at loc and to the right that is open.
Returns false if it reaches max size first.
"""
wave = [loc+vector3d(0,i,0) for i in range(width)]
self.width = width
# Don't expand outside the bounding box
if wave[0].y > self.ur.y:
return None
# Increment while the wave is blocked
while self.is_wave_blocked(wave):
# Or until we cannot increment further
if not self.increment_wave(wave):
return None
return wave
def is_wave_blocked(self, wave):
"""
Checks if any of the locations are blocked
"""
for v in wave:
if self.is_blocked(v):
return True
else:
return False
def increment_wave(self, wave):
"""
Increment the head by moving one step right. Return
new wave if successful.
"""
new_wave = [v+vector3d(1,0,0) for v in wave]
# Don't expand outside the bounding box
if new_wave[0].x>self.ur.x:
return None
if not self.is_wave_blocked(new_wave):
return new_wave
return None
def probe_wave(self, wave):
"""
Expand the wave until there is a blockage and return
the wave path.
"""
wave_path = []
while wave and not self.is_wave_blocked(wave):
wave_path.append(wave)
wave = self.increment_wave(wave)
return wave_path

View File

@ -17,23 +17,21 @@ class supply_router(router):
"""
def __init__(self, gds_name=None, module=None):
"""Use the gds file for the blockages with the top module topName and
"""
Use the gds file for the blockages with the top module topName and
layers for the layers to route on
"""
router.__init__(self, gds_name, module)
self.pins = {}
def clear_pins(self):
def create_routing_grid(self):
"""
Create a sprase routing grid with A* expansion functions.
"""
Convert the routed path to blockages.
Keep the other blockages unchanged.
"""
self.pins = {}
self.rg.reinit()
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
import supply_grid
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
def route(self, cell, layers, vdd_name="vdd", gnd_name="gnd"):
"""
@ -64,7 +62,7 @@ class supply_router(router):
# Now add the blockages (all shapes except the pins)
self.add_blockages()
#self.route_supply_rails()
self.route_supply_rails()
#self.route_supply_pins()
@ -91,13 +89,26 @@ class supply_router(router):
Add supply rails for vdd and gnd alternating in both layers.
Connect cross-over points with vias.
"""
# vdd will be the even grids
# vdd will be the odd grids
vdd_rails = self.route_supply_rail(name="vdd",offset=0,width=2)
# gnd will be the odd grids
# gnd will be the even grids (0 + width)
gnd_rails = self.route_supply_rail(name="gnd",offset=0,width=2)
pass
def route_supply_rail(self, name, offset=0, width=1):
"""
Add supply rails alternating layers.
"""
wave = self.rg.start_wave(loc=vector3d(0,offset,0), width=width)
wave_path = self.rg.probe_wave(wave)
self.add_wave(name, wave_path)
def route_supply_pins(self, pin):
"""
This will route all the supply pins to supply rails one at a time.
@ -145,17 +156,6 @@ class supply_router(router):
self.cell.add_route(self.layers,abs_path)
def create_routing_grid(self):
"""
Create a sprase routing grid with A* expansion functions.
"""
# We will add a halo around the boundary
# of this many tracks
size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y))
import supply_grid
self.rg = supply_grid.supply_grid()

View File

@ -15,16 +15,16 @@ class vector3d():
self.x = x[0]
self.y = x[1]
self.z = x[2]
#will take two inputs as the values of a coordinate
#will take inputs as the values of a coordinate
else:
self.x = x
self.y = y
self.z = z
self.tpl=(x,y,z)
def __str__(self):
""" override print function output """
return "vector3d:["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]"
return "["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]"
def __repr__(self):
""" override print function output """
@ -89,7 +89,7 @@ class vector3d():
Note: This assumes that you DON'T CHANGE THE VECTOR or it will
break things.
"""
return hash(self.tpl)
return hash((self.x,self.y,self.z))
def __rsub__(self, other):
@ -118,6 +118,24 @@ class vector3d():
x_factor=x_factor[0]
return vector3d(self.y*x_factor,self.x*y_factor,self.z*z_factor)
def floor(self):
"""
Override floor function
"""
return vector3d(int(math.floor(self.x)),int(math.floor(self.y)), self.z)
def ceil(self):
"""
Override ceil function
"""
return vector3d(int(math.ceil(self.x)),int(math.ceil(self.y)), self.z)
def round(self):
"""
Override round function
"""
return vector3d(int(round(self.x)),int(round(self.y)), self.z)
def __eq__(self, other):
"""Override the default Equals behavior"""
if isinstance(other, self.__class__):