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):
""" override print function output """
return "["+str(self.x)+","+str(self.y)+"]"
return "v["+str(self.x)+","+str(self.y)+"]"
def __repr__(self):
""" override print function output """
return "["+str(self.x)+","+str(self.y)+"]"
return "v["+str(self.x)+","+str(self.y)+"]"
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 string
from itertools import tee
import debug
from vector3d import vector3d
from cell import cell
import os
from grid_cell import grid_cell
class grid:
"""
A two layer routing map. Each cell can be blocked in the vertical
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):
""" 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
self.source = []
self.target = []
@ -47,8 +45,16 @@ class grid:
self.map[n].blocked=value
def is_blocked(self,n):
self.add_map(n)
return self.map[n].blocked
if isinstance(n,list):
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):
if isinstance(n,list):
@ -98,6 +104,7 @@ class grid:
for n in track_list:
debug.info(3,"Adding source ={0}".format(str(n)))
self.set_source(n)
self.set_blocked(n,False)
def add_target(self,track_list):
@ -105,6 +112,7 @@ class grid:
for n in track_list:
debug.info(3,"Adding target ={0}".format(str(n)))
self.set_target(n)
self.set_blocked(n,False)
def is_target(self,point):
"""
@ -121,52 +129,17 @@ class grid:
self.add_map(item)
else:
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):
"""
Mark the path in the routing grid as blocked.
Also unsets the path flag.
"""
for p in path:
self.set_path(p,False)
self.set_blocked(p)
path.set_path(False)
path.set_blocked(True)
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,
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
self.pins = {}
# A map of pin names to pin structures
self.pins_grids = {}
# A list of pin blockages (represented by the pin structures too)
self.blockages=[]
@ -75,16 +77,17 @@ class router:
elif zindex==0:
return self.horiz_layer_name
else:
debug.error(-1,"Invalid zindex {}".format(zindex))
debug.error("Invalid zindex {}".format(zindex),-1)
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):
"""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
vertical.
"""
@ -117,7 +120,6 @@ class router:
def find_pin(self,pin_name):
"""
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
if they are not actually a blockage.
"""
#for layer in [self.vert_layer_number,self.horiz_layer_number]:
# self.get_blockages(layer)
self.get_blockages(self.horiz_layer_number)
for layer in [self.vert_layer_number,self.horiz_layer_number]:
self.get_blockages(layer)
def clear_pins(self):
"""
@ -209,23 +210,6 @@ class router:
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):
"""
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.
"""
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()
# 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),
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=type_off)
self.cell.add_label(text="{0},{1}".format(g[0],g[1]),
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)
offset=shape[0],
zoom=0.05)
def prepare_path(self,path):
@ -526,7 +512,7 @@ class router:
self.paths.append(path)
# This is marked for debug
self.rg.add_path(path)
path.set_path()
# For debugging... if the path failed to route.
if False or path==None:
@ -548,24 +534,35 @@ class router:
path=self.prepare_path(path)
# 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))
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.
"""
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))
ur = abs_path[-1][-1]
ll = abs_path[0][0]
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),
width=ur.x-ll.x,
height=ur.y-ll.y)
@ -596,8 +593,8 @@ class router:
# 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])
next_inertia=self.get_inertia(path[i],path[i+1])
prev_inertia=self.get_inertia(path[i-1][0],path[i][0])
next_inertia=self.get_inertia(path[i][0],path[i+1][0])
# if we switch directions, add the point, otherwise don't
if prev_inertia!=next_inertia:
newpath.append(path[i])

View File

@ -1,17 +1,20 @@
from itertools import tee
import debug
from vector3d import vector3d
import grid
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.
"""
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, ll, ur, track_factor)
grid.__init__(self, ll, ur, track_factor)
# priority queue for the maze routing
self.q = []
@ -47,18 +50,19 @@ class signal_grid(grid.grid):
for s in self.source:
cost = self.cost_to_target(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
def route(self,detour_scale):
"""
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
# 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
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
while len(self.q)>0:
# 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(3,"Expanding: cost=" + str(cost) + " " + str(path))
debug.info(3,"Expanding: cost=" + str(cost) + " " + str(curpath))
# expand the last element
neighbors = self.expand_dirs(path)
neighbors = self.expand_dirs(curpath)
debug.info(3,"Neighbors: " + str(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
newpath = path + [n]
newpath.append(n)
# check if we hit the target and are done
if self.is_target(n):
return (newpath,self.cost(newpath))
elif not self.map[n].visited:
if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide
return (newpath,newpath.cost())
else:
# current path cost + predicted cost
current_cost = self.cost(newpath)
target_cost = self.cost_to_target(n)
current_cost = newpath.cost()
target_cost = self.cost_to_target(n[0])
predicted_cost = current_cost + target_cost
# only add the cost if it is less than our bound
if (predicted_cost < cost_bound):
if (self.map[n].min_cost==-1 or current_cost<self.map[n].min_cost):
self.map[n].visited=True
self.map[n].min_path = newpath
self.map[n].min_cost = predicted_cost
if (self.map[n[0]].min_cost==-1 or predicted_cost<self.map[n[0]].min_cost):
self.map[n[0]].min_path = newpath
self.map[n[0]].min_cost = predicted_cost
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
heappush(self.q,(predicted_cost,self.counter,newpath))
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.")
return (None,None)
def expand_dirs(self,path):
def expand_dirs(self,curpath):
"""
Expand each of the four cardinal directions plus up or down
but not expanding to blocked cells. Expands in all directions
regardless of preferred directions.
"""
# expand from the last point
point = path[-1]
neighbors = []
east = point + vector3d(1,0,0)
if not self.is_blocked(east) and not east in path:
neighbors.append(east)
# Expand all directions.
neighbors = curpath.expand_dirs()
west= point + vector3d(-1,0,0)
if not self.is_blocked(west) and not west in path:
neighbors.append(west)
# Filter the blocked ones
unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)]
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
return unblocked_neighbors
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.z-dest.z),abs(dest.z-src.z))
if src.x!=dest.x or src.y!=dest.y:
hpwl += self.VIA_COST
hpwl += grid.VIA_COST
return hpwl
def cost_to_target(self,source):
@ -164,37 +151,10 @@ class signal_grid(grid.grid):
cost = self.hpwl(source,self.target[0])
for t in self.target:
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
def get_inertia(self,p0,p1):
"""
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(2,str(path))
self.add_route(path)
return True
else:
self.write_debug_gds()
# clean up so we can try a reroute
self.clear_pins()
return False
return False
self.write_debug_gds()
return True

View File

@ -1,9 +1,11 @@
import debug
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
or horizontal layer.
@ -11,7 +13,7 @@ class supply_grid(grid.grid):
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, ll, ur, track_width)
grid.__init__(self, ll, ur, track_width)
# Current rail
self.rail = []
@ -24,48 +26,27 @@ class supply_grid(grid.grid):
p.reset()
def find_horizontal_start_wave(self, loc, width):
"""
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):
def find_start_wave(self, wave, width, direct):
"""
Finds the first loc starting at loc and up that is open.
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
if wave[0].x > self.ur.x:
return None
if wave[-1].y > self.ur.y:
return None
# Increment while the wave is blocked
if self.is_wave_blocked(wave):
while wave:
wave=self.increment_up_wave(wave)
if not self.is_wave_blocked(wave):
return wave
while wave and self.is_wave_blocked(wave):
wf=grid_path(wave)
wave=wf.neighbor(direct)
# Bail out if we couldn't increment futher
if wave[0].x > self.ur.x or wave[-1].y > self.ur.y:
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
@ -79,55 +60,19 @@ class supply_grid(grid.grid):
else:
return False
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):
def probe(self, wave, direct):
"""
Expand the wave until there is a blockage and return
the wave path.
"""
wave_path = []
wave_path = grid_path()
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 = self.increment_east_wave(wave)
wave = wave_path.neighbor(direct)
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
import math
import debug
from globals import OPTS
import grid
from pin_layout import pin_layout
from vector import vector
from vector3d import vector3d
from globals import OPTS
from router import router
from direction import direction
class supply_router(router):
"""
@ -64,8 +65,8 @@ class supply_router(router):
self.add_blockages()
self.route_supply_rails()
#self.route_supply_pins()
self.connect_supply_rails()
#self.route_pins_to_rails()
# source pin will be a specific layout pin
# target pin will be the rails only
@ -85,107 +86,113 @@ class supply_router(router):
self.write_debug_gds()
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):
"""
Add supply rails for vdd and gnd alternating in both layers.
Connect cross-over points with vias.
"""
# width in grid units
width = 2
# Width in grid units.
self.rail_track_width = 2
# List of all the rails
self.rails = []
self.wave_paths = []
# Keep a list of all the rail wavepaths
self.paths = []
# vdd will be the even grids every 2 widths
for offset in range(0, self.rg.ur.y, 2*width):
loc = vector3d(0,offset,0)
for offset in range(0, self.rg.ur.y, 2*self.rail_track_width):
# 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 loc and loc.x < self.rg.ur.x:
loc = self.route_horizontal_supply_rail("vdd",loc,width)
# gnd will be the odd grids every 2 widths
for offset in range(width, self.rg.ur.y, 2*width):
loc = vector3d(0,offset,0)
while wave and wave[0].x < self.rg.ur.x:
wave = self.route_supply_rail("vdd", wave, direction.EAST)
# gnd will be the even grids every 2 widths
for offset in range(self.rail_track_width, self.rg.ur.y, 2*self.rail_track_width):
# 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 loc and loc.x < self.rg.ur.x:
loc = self.route_horizontal_supply_rail("gnd",loc,width)
while wave and wave[0].x < self.rg.ur.x:
wave = self.route_supply_rail("gnd", wave, direction.EAST)
# vdd will be the even grids every 2 widths
for offset in range(0, self.rg.ur.x, 2*width):
loc = vector3d(offset,0,0)
# While we can keep expanding up
while loc and loc.y < self.rg.ur.y:
loc = self.route_vertical_supply_rail("vdd",loc,width)
# gnd will be the odd grids every 2 widths
for offset in range(width, self.rg.ur.x, 2*width):
loc = vector3d(offset,0,0)
# While we can keep expanding up
while loc and loc.y < self.rg.ur.y:
loc = self.route_vertical_supply_rail("gnd",loc,width)
for offset in range(0, self.rg.ur.x, 2*self.rail_track_width):
# Seed the function at the location with the given width
wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
# While we can keep expanding east
while wave and wave[0].y < self.rg.ur.y:
wave = self.route_supply_rail("vdd", wave, direction.NORTH)
def route_horizontal_supply_rail(self, name, loc, width):
# gnd will be the even grids every 2 widths
for offset in range(self.rail_track_width, self.rg.ur.x, 2*self.rail_track_width):
# Seed the function at the location with the given width
wave = [vector3d(offset+i,0,1) for i in range(self.rail_track_width)]
# While we can keep expanding east
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):
"""
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_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:
return None
# 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:
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)
# Filter any path that won't span 2 rails
# so that we can guarantee it is connected
if len(wave_path)>=2*self.rail_track_width:
self.add_wavepath(name, wave_path)
wave_path.name = name
self.paths.append(wave_path)
# seed the next start wave location
wave_end = wave_path[-1]
next_seed = wave_end[0]+vector3d(1,0,0)
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
return wave_path.neighbor(direct)
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.
After each one, it adds the cells to the blockage list.

View File

@ -24,11 +24,11 @@ class vector3d():
def __str__(self):
""" 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):
""" 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):
"""
@ -139,7 +139,7 @@ class vector3d():
def __eq__(self, other):
"""Override the default Equals behavior"""
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
def __ne__(self, other):