mirror of https://github.com/VLSIDA/OpenRAM.git
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:
parent
c2c17a33d2
commit
69261a0dc1
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
from enum import Enum
|
||||
|
||||
class direction(Enum):
|
||||
NORTH = 1
|
||||
SOUTH = 2
|
||||
EAST = 3
|
||||
WEST = 4
|
||||
UP = 5
|
||||
DOWN = 6
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
class cell:
|
||||
class grid_cell:
|
||||
"""
|
||||
A single cell that can be occupied in a given layer, blocked,
|
||||
visited, etc.
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in New Issue