Routing and connecting rails with vias done.

Refactored grid path class.
Added direction enum.
Does not route multi-track width wires in signal router.
This commit is contained in:
Matt Guthaus 2018-09-07 14:46:58 -07:00
parent c2c17a33d2
commit 69261a0dc1
11 changed files with 445 additions and 359 deletions

View File

@ -24,11 +24,11 @@ class vector():
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "["+str(self.x)+","+str(self.y)+"]" return "v["+str(self.x)+","+str(self.y)+"]"
def __repr__(self): def __repr__(self):
""" override print function output """ """ override print function output """
return "["+str(self.x)+","+str(self.y)+"]" return "v["+str(self.x)+","+str(self.y)+"]"
def __setitem__(self, index, value): def __setitem__(self, index, value):
""" """

View File

@ -0,0 +1,9 @@
from enum import Enum
class direction(Enum):
NORTH = 1
SOUTH = 2
EAST = 3
WEST = 4
UP = 5
DOWN = 6

View File

@ -1,27 +1,25 @@
import numpy as np import numpy as np
import string import string
from itertools import tee
import debug import debug
from vector3d import vector3d from vector3d import vector3d
from cell import cell from grid_cell import grid_cell
import os
class grid: class grid:
""" """
A two layer routing map. Each cell can be blocked in the vertical A two layer routing map. Each cell can be blocked in the vertical
or horizontal layer. or horizontal layer.
""" """
# costs are relative to a unit grid
# non-preferred cost allows an off-direction jog of 1 grid
# rather than 2 vias + preferred direction (cost 5)
VIA_COST = 2
NONPREFERRED_COST = 4
PREFERRED_COST = 1
def __init__(self, ll, ur, track_width): def __init__(self, ll, ur, track_width):
""" Initialize the map and define the costs. """ """ Initialize the map and define the costs. """
# costs are relative to a unit grid
# non-preferred cost allows an off-direction jog of 1 grid
# rather than 2 vias + preferred direction (cost 5)
self.VIA_COST = 2
self.NONPREFERRED_COST = 4
self.PREFERRED_COST = 1
# list of the source/target grid coordinates # list of the source/target grid coordinates
self.source = [] self.source = []
self.target = [] self.target = []
@ -47,8 +45,16 @@ class grid:
self.map[n].blocked=value self.map[n].blocked=value
def is_blocked(self,n): def is_blocked(self,n):
self.add_map(n) if isinstance(n,list):
return self.map[n].blocked for item in n:
if self.is_blocked(item):
return True
else:
return False
else:
self.add_map(n)
return self.map[n].blocked
def set_path(self,n,value=True): def set_path(self,n,value=True):
if isinstance(n,list): if isinstance(n,list):
@ -98,6 +104,7 @@ class grid:
for n in track_list: for n in track_list:
debug.info(3,"Adding source ={0}".format(str(n))) debug.info(3,"Adding source ={0}".format(str(n)))
self.set_source(n) self.set_source(n)
self.set_blocked(n,False)
def add_target(self,track_list): def add_target(self,track_list):
@ -105,6 +112,7 @@ class grid:
for n in track_list: for n in track_list:
debug.info(3,"Adding target ={0}".format(str(n))) debug.info(3,"Adding target ={0}".format(str(n)))
self.set_target(n) self.set_target(n)
self.set_blocked(n,False)
def is_target(self,point): def is_target(self,point):
""" """
@ -121,52 +129,17 @@ class grid:
self.add_map(item) self.add_map(item)
else: else:
if n not in self.map.keys(): if n not in self.map.keys():
self.map[n]=cell() self.map[n]=grid_cell()
def add_path(self,path):
"""
Mark the path in the routing grid for visualization
"""
self.path=path
for p in path:
self.set_path(p)
def block_path(self,path): def block_path(self,path):
""" """
Mark the path in the routing grid as blocked. Mark the path in the routing grid as blocked.
Also unsets the path flag. Also unsets the path flag.
""" """
for p in path: path.set_path(False)
self.set_path(p,False) path.set_blocked(True)
self.set_blocked(p)
def cost(self,path):
"""
The cost of the path is the length plus a penalty for the number
of vias. We assume that non-preferred direction is penalized.
"""
# Ignore the source pin layer change, FIXME?
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
plist = pairwise(path)
cost = 0
for p0,p1 in plist:
if p0.z != p1.z: # via
cost += self.VIA_COST
elif p0.x != p1.x: # horizontal
cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST
elif p0.y != p1.y: # vertical
cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST
else:
debug.error("Non-changing direction!")
return cost

View File

@ -1,4 +1,4 @@
class cell: class grid_cell:
""" """
A single cell that can be occupied in a given layer, blocked, A single cell that can be occupied in a given layer, blocked,
visited, etc. visited, etc.

View File

@ -0,0 +1,194 @@
import debug
from vector3d import vector3d
from itertools import tee
from grid import grid
from grid_cell import grid_cell
from direction import direction
class grid_path:
"""
A grid path is a list of lists of grid cells.
It can have a width that is more than one cell.
All of the sublists will be the same dimension.
Cells should be continguous.
It can have a name to define pin shapes as well.
"""
def __init__(self, items=[], name=""):
self.name = name
if items:
self.pathlist = [items]
else:
self.pathlist = []
def __str__(self):
#import pprint
p = str(self.pathlist) #pprint.pformat(self.pathlist)
if self.name != "":
return (str(self.name) + " : " + p)
return p
def __setitem__(self, index, value):
"""
override setitem function
can set value by pathinstance[index]=value
"""
self.pathlist[index]=value
def __getitem__(self, index):
"""
override getitem function
can get value by value=pathinstance[index]
"""
return self.pathlist[index]
def __contains__(self, key):
"""
Determine if cell exists in this path
"""
# FIXME: Could maintain a hash to make in O(1)
for sublist in self.pathlist:
for item in sublist:
if item == key:
return True
else:
return False
def __add__(self, items):
"""
Override add to do append
"""
return self.pathlist.extend(items)
def __len__(self):
return len(self.pathlist)
def append(self,item):
"""
Append the list of items to the cells
"""
self.pathlist.append(item)
def extend(self,item):
"""
Extend the list of items to the cells
"""
self.pathlist.extend(item)
def set_path(self,value=True):
for sublist in self.pathlist:
for p in sublist:
p.path=value
def set_blocked(self,value=True):
for sublist in self.pathlist:
for p in sublist:
p.blocked=value
def cost(self):
"""
The cost of the path is the length plus a penalty for the number
of vias. We assume that non-preferred direction is penalized.
This cost only works with 1 wide tracks.
"""
# Ignore the source pin layer change, FIXME?
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
plist = list(pairwise(self.pathlist))
cost = 0
for p0list,p1list in plist:
# This is because they are "waves" so pick the first item
p0=p0list[0]
p1=p1list[0]
if p0.z != p1.z: # via
cost += grid.VIA_COST
elif p0.x != p1.x and p0.z==1: # horizontal on vertical layer
cost += grid.NONPREFERRED_COST
elif p0.y != p1.y and p0.z==0: # vertical on horizontal layer
cost += grid.NONPREFERRED_COST
else:
cost += grid.PREFERRED_COST
return cost
def expand_dirs(self,up_down_too=True):
"""
Expand from the end in each of the four cardinal directions plus up
or down but not expanding to blocked cells. Expands in all
directions regardless of preferred directions.
If the width is more than one, it can only expand in one direction
(for now). This is assumed for the supply router for now.
"""
neighbors = []
for d in list(direction):
if not up_down_too and (d==direction.UP or d==direction.DOWN):
continue
n = self.neighbor(d)
if n:
neighbors.append(n)
return neighbors
def neighbor(self, d):
if d==direction.EAST:
offset = vector3d(1,0,0)
elif d==direction.WEST:
offset = vector3d(-1,0,0)
elif d==direction.NORTH:
offset = vector3d(0,1,0)
elif d==direction.SOUTH:
offset = vector3d(0,-1,0)
elif d==direction.UP:
offset = vector3d(0,0,1)
elif d==direction.DOWN:
offset = vector3d(0,0,-1)
else:
debug.error("Invalid direction {}".format(d),-1)
newwave = [point + offset for point in self.pathlist[-1]]
if newwave in self.pathlist:
return None
elif newwave[0].z>1 or newwave[0].z<0:
return None
return newwave
def set_layer(self, zindex):
new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave]
self.pathlist = new_pathlist
def overlap(self, other):
"""
Return the overlap waves ignoring different layers
"""
my_zindex = self.pathlist[0][0].z
other_flat_cells = [vector3d(item.x,item.y,my_zindex) for wave in other.pathlist for item in wave]
# This keeps the wave structure of the self layer
shared_waves = []
for wave in self.pathlist:
for item in wave:
# If any item in the wave is not contained, skip it
if not item in other_flat_cells:
break
else:
shared_waves.append(wave)
if len(shared_waves)>0:
ll = shared_waves[0][0]
ur = shared_waves[-1][-1]
return [ll,ur]
return None

View File

@ -37,6 +37,8 @@ class router:
# A map of pin names to pin structures # A map of pin names to pin structures
self.pins = {} self.pins = {}
# A map of pin names to pin structures
self.pins_grids = {}
# A list of pin blockages (represented by the pin structures too) # A list of pin blockages (represented by the pin structures too)
self.blockages=[] self.blockages=[]
@ -75,16 +77,17 @@ class router:
elif zindex==0: elif zindex==0:
return self.horiz_layer_name return self.horiz_layer_name
else: else:
debug.error(-1,"Invalid zindex {}".format(zindex)) debug.error("Invalid zindex {}".format(zindex),-1)
def is_wave(self,path): def is_wave(self,path):
""" """
Determines if this is a wave (True) or a normal route (False) Determines if this is a multi-track width wave (True) or a normal route (False)
""" """
return isinstance(path[0],list) return len(path[0])>1
def set_layers(self, layers): def set_layers(self, layers):
"""Allows us to change the layers that we are routing on. First layer """
Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always is always horizontal, middle is via, and last is always
vertical. vertical.
""" """
@ -117,7 +120,6 @@ class router:
def find_pin(self,pin_name): def find_pin(self,pin_name):
""" """
Finds the pin shapes and converts to tracks. Finds the pin shapes and converts to tracks.
@ -144,9 +146,8 @@ class router:
This doesn't consider whether the obstacles will be pins or not. They get reset later This doesn't consider whether the obstacles will be pins or not. They get reset later
if they are not actually a blockage. if they are not actually a blockage.
""" """
#for layer in [self.vert_layer_number,self.horiz_layer_number]: for layer in [self.vert_layer_number,self.horiz_layer_number]:
# self.get_blockages(layer) self.get_blockages(layer)
self.get_blockages(self.horiz_layer_number)
def clear_pins(self): def clear_pins(self):
""" """
@ -209,23 +210,6 @@ class router:
return 2 return 2
def contract_path(self,path):
"""
Remove intermediate points in a rectilinear path.
"""
newpath = [path[0]]
for i in range(1,len(path)-1):
prev_inertia=self.get_inertia(path[i-1],path[i])
next_inertia=self.get_inertia(path[i],path[i+1])
# if we switch directions, add the point, otherwise don't
if prev_inertia!=next_inertia:
newpath.append(path[i])
# always add the last path
newpath.append(path[-1])
return newpath
def add_path_blockages(self): def add_path_blockages(self):
""" """
Go through all of the past paths and add them as blockages. Go through all of the past paths and add them as blockages.
@ -472,48 +456,50 @@ class router:
called once or the labels will overlap. called once or the labels will overlap.
""" """
debug.info(0,"Adding router info") debug.info(0,"Adding router info")
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() 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)
# midpoint offset t=self.rg.map[g].get_type()
off=vector((shape[1].x+shape[0].x)/2,
(shape[1].y+shape[0].y)/2) # midpoint offset
if g[2]==1: off=vector((shape[1].x+shape[0].x)/2,
# Upper layer is upper right label (shape[1].y+shape[0].y)/2)
type_off=off+partial_track if g[2]==1:
else: # Upper layer is upper right label
# Lower layer is lower left label type_off=off+partial_track
type_off=off-partial_track else:
if t!=None: # Lower layer is lower left label
self.cell.add_label(text=str(t), 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", layer="text",
offset=type_off) offset=shape[0],
self.cell.add_label(text="{0},{1}".format(g[0],g[1]), zoom=0.05)
layer="text",
offset=shape[0],
zoom=0.05)
for blockage in self.blockages:
# Display the inflated blockage
(ll,ur) = blockage.inflate()
self.cell.add_rect(layer="blockage",
offset=ll,
width=ur.x-ll.x,
height=ur.y-ll.y)
def prepare_path(self,path): def prepare_path(self,path):
@ -526,7 +512,7 @@ class router:
self.paths.append(path) self.paths.append(path)
# This is marked for debug # This is marked for debug
self.rg.add_path(path) path.set_path()
# For debugging... if the path failed to route. # For debugging... if the path failed to route.
if False or path==None: if False or path==None:
@ -548,12 +534,23 @@ class router:
path=self.prepare_path(path) path=self.prepare_path(path)
# convert the path back to absolute units from tracks # convert the path back to absolute units from tracks
abs_path = list(map(self.convert_point_to_units,path)) # This assumes 1-track wide again
abs_path = [self.convert_point_to_units(x[0]) for x in path]
debug.info(1,str(abs_path)) debug.info(1,str(abs_path))
self.cell.add_route(self.layers,abs_path) self.cell.add_route(self.layers,abs_path)
def add_via(self,loc,size=1):
"""
Add a via centered at the current location
"""
loc = self.convert_point_to_units(vector3d(loc[0],loc[1],0))
self.cell.add_via_center(layers=self.layers,
offset=vector(loc.x,loc.y),
size=(size,size))
def add_wave(self, name, path):
def add_wavepath(self, name, path):
""" """
Add the current wave to the given design instance. Add the current wave to the given design instance.
""" """
@ -561,11 +558,11 @@ class router:
# convert the path back to absolute units from tracks # convert the path back to absolute units from tracks
abs_path = [self.convert_wave_to_units(i) for i in path] abs_path = [self.convert_wave_to_units(i) for i in path]
#debug.info(1,str(abs_path))
ur = abs_path[-1][-1] ur = abs_path[-1][-1]
ll = abs_path[0][0] ll = abs_path[0][0]
pin = self.cell.add_layout_pin(name, pin = self.cell.add_layout_pin(name,
layer=self.get_layer(ll.z), layer=self.get_layer(path[0][0].z),
offset=vector(ll.x,ll.y), offset=vector(ll.x,ll.y),
width=ur.x-ll.x, width=ur.x-ll.x,
height=ur.y-ll.y) height=ur.y-ll.y)
@ -596,8 +593,8 @@ class router:
# Make a list only of points that change inertia of the path # Make a list only of points that change inertia of the path
newpath = [path[0]] newpath = [path[0]]
for i in range(1,len(path)-1): for i in range(1,len(path)-1):
prev_inertia=self.get_inertia(path[i-1],path[i]) prev_inertia=self.get_inertia(path[i-1][0],path[i][0])
next_inertia=self.get_inertia(path[i],path[i+1]) next_inertia=self.get_inertia(path[i][0],path[i+1][0])
# if we switch directions, add the point, otherwise don't # if we switch directions, add the point, otherwise don't
if prev_inertia!=next_inertia: if prev_inertia!=next_inertia:
newpath.append(path[i]) newpath.append(path[i])

View File

@ -1,17 +1,20 @@
from itertools import tee from itertools import tee
import debug import debug
from vector3d import vector3d
import grid
from heapq import heappush,heappop from heapq import heappush,heappop
from copy import deepcopy
class signal_grid(grid.grid): from grid import grid
from grid_path import grid_path
from vector3d import vector3d
class signal_grid(grid):
""" """
Expand the two layer grid to include A* search functions for a source and target. Expand the two layer grid to include A* search functions for a source and target.
""" """
def __init__(self, ll, ur, track_factor): def __init__(self, ll, ur, track_factor):
""" Create a routing map of width x height cells and 2 in the z-axis. """ """ Create a routing map of width x height cells and 2 in the z-axis. """
grid.grid.__init__(self, ll, ur, track_factor) grid.__init__(self, ll, ur, track_factor)
# priority queue for the maze routing # priority queue for the maze routing
self.q = [] self.q = []
@ -47,18 +50,19 @@ class signal_grid(grid.grid):
for s in self.source: for s in self.source:
cost = self.cost_to_target(s) cost = self.cost_to_target(s)
debug.info(2,"Init: cost=" + str(cost) + " " + str([s])) debug.info(2,"Init: cost=" + str(cost) + " " + str([s]))
heappush(self.q,(cost,self.counter,[s])) heappush(self.q,(cost,self.counter,grid_path([vector3d(s)])))
self.counter+=1 self.counter+=1
def route(self,detour_scale): def route(self,detour_scale):
""" """
This does the A* maze routing with preferred direction routing. This does the A* maze routing with preferred direction routing.
This only works for 1 track wide routes!
""" """
# We set a cost bound of the HPWL for run-time. This can be # We set a cost bound of the HPWL for run-time. This can be
# over-ridden if the route fails due to pruning a feasible solution. # over-ridden if the route fails due to pruning a feasible solution.
cost_bound = detour_scale*self.cost_to_target(self.source[0])*self.PREFERRED_COST cost_bound = detour_scale*self.cost_to_target(self.source[0])*grid.PREFERRED_COST
# Make sure the queue is empty if we run another route # Make sure the queue is empty if we run another route
while len(self.q)>0: while len(self.q)>0:
@ -72,75 +76,58 @@ class signal_grid(grid.grid):
# Keep expanding and adding to the priority queue until we are done # Keep expanding and adding to the priority queue until we are done
while len(self.q)>0: while len(self.q)>0:
# should we keep the path in the queue as well or just the final node? # should we keep the path in the queue as well or just the final node?
(cost,count,path) = heappop(self.q) (cost,count,curpath) = heappop(self.q)
debug.info(2,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) debug.info(2,"Queue size: size=" + str(len(self.q)) + " " + str(cost))
debug.info(3,"Expanding: cost=" + str(cost) + " " + str(path)) debug.info(3,"Expanding: cost=" + str(cost) + " " + str(curpath))
# expand the last element # expand the last element
neighbors = self.expand_dirs(path) neighbors = self.expand_dirs(curpath)
debug.info(3,"Neighbors: " + str(neighbors)) debug.info(3,"Neighbors: " + str(neighbors))
for n in neighbors: for n in neighbors:
# make a new copy of the path to not update the old ones
newpath = deepcopy(curpath)
# node is added to the map by the expand routine # node is added to the map by the expand routine
newpath = path + [n] newpath.append(n)
# check if we hit the target and are done # check if we hit the target and are done
if self.is_target(n): if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide
return (newpath,self.cost(newpath)) return (newpath,newpath.cost())
elif not self.map[n].visited: else:
# current path cost + predicted cost # current path cost + predicted cost
current_cost = self.cost(newpath) current_cost = newpath.cost()
target_cost = self.cost_to_target(n) target_cost = self.cost_to_target(n[0])
predicted_cost = current_cost + target_cost predicted_cost = current_cost + target_cost
# only add the cost if it is less than our bound # only add the cost if it is less than our bound
if (predicted_cost < cost_bound): if (predicted_cost < cost_bound):
if (self.map[n].min_cost==-1 or current_cost<self.map[n].min_cost): if (self.map[n[0]].min_cost==-1 or predicted_cost<self.map[n[0]].min_cost):
self.map[n].visited=True self.map[n[0]].min_path = newpath
self.map[n].min_path = newpath self.map[n[0]].min_cost = predicted_cost
self.map[n].min_cost = predicted_cost
debug.info(3,"Enqueuing: cost=" + str(current_cost) + "+" + str(target_cost) + " " + str(newpath)) debug.info(3,"Enqueuing: cost=" + str(current_cost) + "+" + str(target_cost) + " " + str(newpath))
# add the cost to get to this point if we haven't reached it yet # add the cost to get to this point if we haven't reached it yet
heappush(self.q,(predicted_cost,self.counter,newpath)) heappush(self.q,(predicted_cost,self.counter,newpath))
self.counter += 1 self.counter += 1
#else:
# print("Better previous cost.")
#else:
# print("Cost bounded")
debug.warning("Unable to route path. Expand the detour_scale to allow detours.") debug.warning("Unable to route path. Expand the detour_scale to allow detours.")
return (None,None) return (None,None)
def expand_dirs(self,path): def expand_dirs(self,curpath):
""" """
Expand each of the four cardinal directions plus up or down Expand each of the four cardinal directions plus up or down
but not expanding to blocked cells. Expands in all directions but not expanding to blocked cells. Expands in all directions
regardless of preferred directions. regardless of preferred directions.
""" """
# expand from the last point
point = path[-1]
neighbors = [] # Expand all directions.
neighbors = curpath.expand_dirs()
east = point + vector3d(1,0,0) # Filter the blocked ones
if not self.is_blocked(east) and not east in path: unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)]
neighbors.append(east)
west= point + vector3d(-1,0,0) return unblocked_neighbors
if not self.is_blocked(west) and not west in path:
neighbors.append(west)
up = point + vector3d(0,0,1)
if up.z<2 and not self.is_blocked(up) and not up in path:
neighbors.append(up)
north = point + vector3d(0,1,0)
if not self.is_blocked(north) and not north in path:
neighbors.append(north)
south = point + vector3d(0,-1,0)
if not self.is_blocked(south) and not south in path:
neighbors.append(south)
down = point + vector3d(0,0,-1)
if down.z>=0 and not self.is_blocked(down) and not down in path:
neighbors.append(down)
return neighbors
def hpwl(self, src, dest): def hpwl(self, src, dest):
@ -153,7 +140,7 @@ class signal_grid(grid.grid):
hpwl += max(abs(src.y-dest.y),abs(dest.y-src.y)) hpwl += max(abs(src.y-dest.y),abs(dest.y-src.y))
hpwl += max(abs(src.z-dest.z),abs(dest.z-src.z)) hpwl += max(abs(src.z-dest.z),abs(dest.z-src.z))
if src.x!=dest.x or src.y!=dest.y: if src.x!=dest.x or src.y!=dest.y:
hpwl += self.VIA_COST hpwl += grid.VIA_COST
return hpwl return hpwl
def cost_to_target(self,source): def cost_to_target(self,source):
@ -164,37 +151,10 @@ class signal_grid(grid.grid):
cost = self.hpwl(source,self.target[0]) cost = self.hpwl(source,self.target[0])
for t in self.target: for t in self.target:
cost = min(self.hpwl(source,t),cost) cost = min(self.hpwl(source,t),cost)
return cost
def cost(self,path):
"""
The cost of the path is the length plus a penalty for the number
of vias. We assume that non-preferred direction is penalized.
"""
# Ignore the source pin layer change, FIXME?
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
plist = pairwise(path)
cost = 0
for p0,p1 in plist:
if p0.z != p1.z: # via
cost += self.VIA_COST
elif p0.x != p1.x: # horizontal
cost += self.NONPREFERRED_COST if (p0.z == 1) else self.PREFERRED_COST
elif p0.y != p1.y: # vertical
cost += self.NONPREFERRED_COST if (p0.z == 0) else self.PREFERRED_COST
else:
debug.error("Non-changing direction!")
return cost return cost
def get_inertia(self,p0,p1): def get_inertia(self,p0,p1):
""" """
Sets the direction based on the previous direction we came from. Sets the direction based on the previous direction we came from.

View File

@ -82,14 +82,15 @@ class signal_router(router):
debug.info(1,"Found path: cost={0} ".format(cost)) debug.info(1,"Found path: cost={0} ".format(cost))
debug.info(2,str(path)) debug.info(2,str(path))
self.add_route(path) self.add_route(path)
return True
else: else:
self.write_debug_gds() self.write_debug_gds()
# clean up so we can try a reroute # clean up so we can try a reroute
self.clear_pins() self.clear_pins()
return False
self.write_debug_gds()
return False return True

View File

@ -1,9 +1,11 @@
import debug import debug
from vector3d import vector3d from vector3d import vector3d
import grid from grid import grid
from grid_path import grid_path
from direction import direction
class supply_grid(grid.grid): class supply_grid(grid):
""" """
A two layer routing map. Each cell can be blocked in the vertical A two layer routing map. Each cell can be blocked in the vertical
or horizontal layer. or horizontal layer.
@ -11,7 +13,7 @@ class supply_grid(grid.grid):
def __init__(self, ll, ur, track_width): def __init__(self, ll, ur, track_width):
""" Create a routing map of width x height cells and 2 in the z-axis. """ """ Create a routing map of width x height cells and 2 in the z-axis. """
grid.grid.__init__(self, ll, ur, track_width) grid.__init__(self, ll, ur, track_width)
# Current rail # Current rail
self.rail = [] self.rail = []
@ -24,48 +26,27 @@ class supply_grid(grid.grid):
p.reset() p.reset()
def find_horizontal_start_wave(self, loc, width): def find_start_wave(self, wave, width, direct):
"""
Finds the first loc starting at loc and to the right that is open.
Returns None 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
if self.is_wave_blocked(wave):
while wave:
wave=self.increment_east_wave(wave)
if not self.is_wave_blocked(wave):
return wave
# This may return None
return wave
def find_vertical_start_wave(self, loc, width):
""" """
Finds the first loc starting at loc and up that is open. Finds the first loc starting at loc and up that is open.
Returns None if it reaches max size first. Returns None if it reaches max size first.
""" """
wave = [loc+vector3d(i,0,1) for i in range(width)]
self.width = width
# Don't expand outside the bounding box # Don't expand outside the bounding box
if wave[0].x > self.ur.x: if wave[0].x > self.ur.x:
return None return None
if wave[-1].y > self.ur.y:
return None
# Increment while the wave is blocked while wave and self.is_wave_blocked(wave):
if self.is_wave_blocked(wave): wf=grid_path(wave)
while wave: wave=wf.neighbor(direct)
wave=self.increment_up_wave(wave) # Bail out if we couldn't increment futher
if not self.is_wave_blocked(wave): if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
return wave return None
# Return a start if it isn't blocked
if not self.is_wave_blocked(wave):
return wave
# This may return None
return wave return wave
@ -80,54 +61,18 @@ class supply_grid(grid.grid):
return False return False
def probe(self, wave, direct):
def increment_east_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
return new_wave
def increment_up_wave(self, wave):
"""
Increment the head by moving one step up. Return
new wave if successful.
"""
new_wave = [v+vector3d(0,1,0) for v in wave]
# Don't expand outside the bounding box
if new_wave[0].y>self.ur.y:
return None
return new_wave
def probe_east_wave(self, wave):
""" """
Expand the wave until there is a blockage and return Expand the wave until there is a blockage and return
the wave path. the wave path.
""" """
wave_path = [] wave_path = grid_path()
while wave and not self.is_wave_blocked(wave): while wave and not self.is_wave_blocked(wave):
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
break
wave_path.append(wave) wave_path.append(wave)
wave = self.increment_east_wave(wave) wave = wave_path.neighbor(direct)
return wave_path return wave_path
def probe_up_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_up_wave(wave)
return wave_path

View File

@ -3,12 +3,13 @@ import tech
from contact import contact from contact import contact
import math import math
import debug import debug
from globals import OPTS
import grid import grid
from pin_layout import pin_layout from pin_layout import pin_layout
from vector import vector from vector import vector
from vector3d import vector3d from vector3d import vector3d
from globals import OPTS
from router import router from router import router
from direction import direction
class supply_router(router): class supply_router(router):
""" """
@ -64,8 +65,8 @@ class supply_router(router):
self.add_blockages() self.add_blockages()
self.route_supply_rails() self.route_supply_rails()
self.connect_supply_rails()
#self.route_supply_pins() #self.route_pins_to_rails()
# source pin will be a specific layout pin # source pin will be a specific layout pin
# target pin will be the rails only # target pin will be the rails only
@ -85,107 +86,113 @@ class supply_router(router):
self.write_debug_gds() self.write_debug_gds()
return False return False
def connect_supply_rails(self):
"""
Add vias between overlapping supply rails.
"""
self.connect_supply_rail("vdd")
self.connect_supply_rail("gnd")
def connect_supply_rail(self, name):
"""
Add vias between overlapping supply rails.
"""
paths = [x for x in self.paths if x.name == name]
# Split into horizontal and vertical
vertical_paths = [x for x in paths if x[0][0].z==1]
horizontal_paths = [x for x in paths if x[0][0].z==0]
shared_areas = []
for v in vertical_paths:
for h in horizontal_paths:
overlap = v.overlap(h)
if overlap:
shared_areas.append(overlap)
for (ll,ur) in shared_areas:
center = (ll + ur).scale(0.5,0.5,0)
self.add_via(center,self.rail_track_width)
def route_supply_rails(self): def route_supply_rails(self):
""" """
Add supply rails for vdd and gnd alternating in both layers. Add supply rails for vdd and gnd alternating in both layers.
Connect cross-over points with vias. Connect cross-over points with vias.
""" """
# width in grid units # Width in grid units.
width = 2 self.rail_track_width = 2
# List of all the rails # Keep a list of all the rail wavepaths
self.rails = [] self.paths = []
self.wave_paths = []
# vdd will be the even grids every 2 widths # vdd will be the even grids every 2 widths
for offset in range(0, self.rg.ur.y, 2*width): for offset in range(0, self.rg.ur.y, 2*self.rail_track_width):
loc = vector3d(0,offset,0) # Seed the function at the location with the given width
wave = [vector3d(0,offset+i,0) for i in range(self.rail_track_width)]
# While we can keep expanding east # While we can keep expanding east
while loc and loc.x < self.rg.ur.x: while wave and wave[0].x < self.rg.ur.x:
loc = self.route_horizontal_supply_rail("vdd",loc,width) wave = self.route_supply_rail("vdd", wave, direction.EAST)
# gnd will be the odd grids every 2 widths # gnd will be the even grids every 2 widths
for offset in range(width, self.rg.ur.y, 2*width): for offset in range(self.rail_track_width, self.rg.ur.y, 2*self.rail_track_width):
loc = vector3d(0,offset,0) # Seed the function at the location with the given width
wave = [vector3d(0,offset+i,0) for i in range(self.rail_track_width)]
# While we can keep expanding east # While we can keep expanding east
while loc and loc.x < self.rg.ur.x: while wave and wave[0].x < self.rg.ur.x:
loc = self.route_horizontal_supply_rail("gnd",loc,width) wave = self.route_supply_rail("gnd", wave, direction.EAST)
# vdd will be the even grids every 2 widths # vdd will be the even grids every 2 widths
for offset in range(0, self.rg.ur.x, 2*width): for offset in range(0, self.rg.ur.x, 2*self.rail_track_width):
loc = vector3d(offset,0,0) # Seed the function at the location with the given width
# While we can keep expanding up wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
while loc and loc.y < self.rg.ur.y: # While we can keep expanding east
loc = self.route_vertical_supply_rail("vdd",loc,width) while wave and wave[0].y < self.rg.ur.y:
wave = self.route_supply_rail("vdd", wave, direction.NORTH)
# gnd will be the odd grids every 2 widths # gnd will be the even grids every 2 widths
for offset in range(width, self.rg.ur.x, 2*width): for offset in range(self.rail_track_width, self.rg.ur.x, 2*self.rail_track_width):
loc = vector3d(offset,0,0) # Seed the function at the location with the given width
# While we can keep expanding up wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
while loc and loc.y < self.rg.ur.y: # While we can keep expanding east
loc = self.route_vertical_supply_rail("gnd",loc,width) while wave and wave[0].y < self.rg.ur.y:
wave = self.route_supply_rail("gnd", wave, direction.NORTH)
def route_supply_rail(self, name, seed_wave, direct):
def route_horizontal_supply_rail(self, name, loc, width):
""" """
Add supply rails alternating layers. Add supply rails alternating layers.
Return the final wavefront for seeding the next wave. Return the final wavefront for seeding the next wave.
""" """
# Sweep to find an initial wave
start_wave = self.rg.find_horizontal_start_wave(loc, width) # Sweep to find an initial unblocked valid wave
start_wave = self.rg.find_start_wave(seed_wave, len(seed_wave), direct)
if not start_wave: if not start_wave:
return None return None
# Expand the wave to the right # Expand the wave to the right
wave_path = self.rg.probe_east_wave(start_wave) wave_path = self.rg.probe(start_wave, direct)
if not wave_path: if not wave_path:
return None return None
# Filter single unit paths # Filter any path that won't span 2 rails
# FIXME: Should we filter bigger sizes? # so that we can guarantee it is connected
if len(wave_path)>1: if len(wave_path)>=2*self.rail_track_width:
new_pin = self.add_wave(name, wave_path) self.add_wavepath(name, wave_path)
self.rails.append(new_pin) wave_path.name = name
self.wave_paths.append(wave_path) self.paths.append(wave_path)
# seed the next start wave location # seed the next start wave location
wave_end = wave_path[-1] wave_end = wave_path[-1]
next_seed = wave_end[0]+vector3d(1,0,0) return wave_path.neighbor(direct)
return next_seed
def route_vertical_supply_rail(self, name, loc, width):
"""
Add supply rails alternating layers.
Return the final wavefront for seeding the next wave.
"""
# Sweep to find an initial wave
start_wave = self.rg.find_vertical_start_wave(loc, width)
if not start_wave:
return None
# Expand the wave to the right
wave_path = self.rg.probe_up_wave(start_wave)
if not wave_path:
return None
# Filter single unit paths
# FIXME: Should we filter bigger sizes?
if len(wave_path)>1:
new_pin = self.add_wave(name, wave_path)
self.rails.append(new_pin)
self.wave_paths.append(wave_path)
# seed the next start wave location
wave_end = wave_path[-1]
next_seed = wave_end[0]+vector3d(0,1,0)
return next_seed
def route_supply_pins(self, pin): def route_pins_to_rails(self):
""" """
This will route all the supply pins to supply rails one at a time. This will route all the supply pins to supply rails one at a time.
After each one, it adds the cells to the blockage list. After each one, it adds the cells to the blockage list.

View File

@ -24,11 +24,11 @@ class vector3d():
def __str__(self): def __str__(self):
""" override print function output """ """ override print function output """
return "["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]"
def __repr__(self): def __repr__(self):
""" override print function output """ """ override print function output """
return "["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]" return "v3d["+str(self.x)+", "+str(self.y)+", "+str(self.z)+"]"
def __setitem__(self, index, value): def __setitem__(self, index, value):
""" """
@ -139,7 +139,7 @@ class vector3d():
def __eq__(self, other): def __eq__(self, other):
"""Override the default Equals behavior""" """Override the default Equals behavior"""
if isinstance(other, self.__class__): if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__ return self.x==other.x and self.y==other.y and self.z==other.z
return False return False
def __ne__(self, other): def __ne__(self, other):