mirror of https://github.com/VLSIDA/OpenRAM.git
Fixed offgrid pins. Added vias to src/dst pins. Added preferred direction routing costs.
This commit is contained in:
parent
0766db9e11
commit
76f338e982
|
|
@ -1,5 +1,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
from itertools import tee
|
||||||
import debug
|
import debug
|
||||||
from vector3d import vector3d
|
from vector3d import vector3d
|
||||||
|
|
||||||
|
|
@ -24,50 +25,52 @@ class grid:
|
||||||
self.target = []
|
self.target = []
|
||||||
self.blocked = []
|
self.blocked = []
|
||||||
self.map={}
|
self.map={}
|
||||||
for x in range(width):
|
|
||||||
for y in range(height):
|
# let's leave this sparse, create cells on demand
|
||||||
for z in range(2):
|
# for x in range(width):
|
||||||
self.map[vector3d(x,y,z)]=cell()
|
# for y in range(height):
|
||||||
|
# for z in range(2):
|
||||||
|
# self.map[vector3d(x,y,z)]=cell()
|
||||||
|
|
||||||
# priority queue for the maze routing
|
# priority queue for the maze routing
|
||||||
self.q = Q.PriorityQueue()
|
self.q = Q.PriorityQueue()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def view(self,filename="test.png"):
|
# def view(self,filename="test.png"):
|
||||||
"""
|
# """
|
||||||
View the data by creating an RGB array and mapping the data
|
# View the data by creating an RGB array and mapping the data
|
||||||
structure to the RGB color palette.
|
# structure to the RGB color palette.
|
||||||
"""
|
# """
|
||||||
|
|
||||||
v_map = np.zeros((self.width,self.height,3), 'uint8')
|
# v_map = np.zeros((self.width,self.height,3), 'uint8')
|
||||||
mid_map = np.ones((10,self.height,3), 'uint8')
|
# mid_map = np.ones((10,self.height,3), 'uint8')
|
||||||
h_map = np.ones((self.width,self.height,3), 'uint8')
|
# h_map = np.ones((self.width,self.height,3), 'uint8')
|
||||||
|
|
||||||
# We shouldn't have a path greater than 50% the HPWL
|
# # We shouldn't have a path greater than 50% the HPWL
|
||||||
# so scale all visited indices by this value for colorization
|
# # so scale all visited indices by this value for colorization
|
||||||
for x in range(self.width):
|
# for x in range(self.width):
|
||||||
for y in range(self.height):
|
# for y in range(self.height):
|
||||||
h_map[x,y] = self.map[vector3d(x,y,0)].get_color()
|
# h_map[x,y] = self.map[vector3d(x,y,0)].get_color()
|
||||||
v_map[x,y] = self.map[vector3d(x,y,1)].get_color()
|
# v_map[x,y] = self.map[vector3d(x,y,1)].get_color()
|
||||||
# This is just for scale
|
# # This is just for scale
|
||||||
if x==0 and y==0:
|
# if x==0 and y==0:
|
||||||
h_map[x,y] = [0,0,0]
|
# h_map[x,y] = [0,0,0]
|
||||||
v_map[x,y] = [0,0,0]
|
# v_map[x,y] = [0,0,0]
|
||||||
|
|
||||||
v_img = Image.fromarray(v_map, 'RGB').rotate(90)
|
# v_img = Image.fromarray(v_map, 'RGB').rotate(90)
|
||||||
#v_img.show()
|
# #v_img.show()
|
||||||
mid_img = Image.fromarray(mid_map, 'RGB').rotate(90)
|
# mid_img = Image.fromarray(mid_map, 'RGB').rotate(90)
|
||||||
h_img = Image.fromarray(h_map, 'RGB').rotate(90)
|
# h_img = Image.fromarray(h_map, 'RGB').rotate(90)
|
||||||
#h_img.show()
|
# #h_img.show()
|
||||||
|
|
||||||
# concatenate them into a plot with the two layers
|
# # concatenate them into a plot with the two layers
|
||||||
img = Image.new('RGB', (2*self.width+10, self.height))
|
# img = Image.new('RGB', (2*self.width+10, self.height))
|
||||||
img.paste(h_img, (0,0))
|
# img.paste(h_img, (0,0))
|
||||||
img.paste(mid_img, (self.width,0))
|
# img.paste(mid_img, (self.width,0))
|
||||||
img.paste(v_img, (self.width+10,0))
|
# img.paste(v_img, (self.width+10,0))
|
||||||
#img.show()
|
# #img.show()
|
||||||
img.save(filename)
|
# img.save(filename)
|
||||||
|
|
||||||
def set_property(self,ll,ur,z,name,value=True):
|
def set_property(self,ll,ur,z,name,value=True):
|
||||||
assert(ur[1] >= ll[1] and ur[0] >= ll[0])
|
assert(ur[1] >= ll[1] and ur[0] >= ll[0])
|
||||||
|
|
@ -77,8 +80,11 @@ class grid:
|
||||||
assert(ur[1]<self.height and ur[1]>=0)
|
assert(ur[1]<self.height and ur[1]>=0)
|
||||||
for x in range(int(ll[0]),int(ur[0])+1):
|
for x in range(int(ll[0]),int(ur[0])+1):
|
||||||
for y in range(int(ll[1]),int(ur[1])+1):
|
for y in range(int(ll[1]),int(ur[1])+1):
|
||||||
setattr (self.map[vector3d(x,y,z)], name, True)
|
n = vector3d(x,y,z)
|
||||||
getattr (self, name).append(vector3d(x,y,z))
|
self.add_map(n)
|
||||||
|
setattr (self.map[n], name, True)
|
||||||
|
if n not in getattr(self, name):
|
||||||
|
getattr(self, name).append(n)
|
||||||
|
|
||||||
def add_blockage(self,ll,ur,z):
|
def add_blockage(self,ll,ur,z):
|
||||||
debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
|
debug.info(3,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
|
||||||
|
|
@ -100,11 +106,16 @@ class grid:
|
||||||
for p in path:
|
for p in path:
|
||||||
self.map[p].path=True
|
self.map[p].path=True
|
||||||
|
|
||||||
def route(self):
|
def route(self,cost_bound=0):
|
||||||
"""
|
"""
|
||||||
This does the A* maze routing.
|
This does the A* maze routing with preferred direction routing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# We set a cost bound of 2.5 x the HPWL for run-time. This can be
|
||||||
|
# over-ridden if the route fails due to pruning a feasible solution.
|
||||||
|
if (cost_bound==0):
|
||||||
|
cost_bound = 2.5*self.cost_to_target(self.source[0])
|
||||||
|
|
||||||
# Make sure the queue is empty if we run another route
|
# Make sure the queue is empty if we run another route
|
||||||
while not self.q.empty():
|
while not self.q.empty():
|
||||||
self.q.get()
|
self.q.get()
|
||||||
|
|
@ -117,7 +128,7 @@ class 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 not self.q.empty():
|
while not self.q.empty():
|
||||||
(cost,path) = self.q.get()
|
(cost,path) = self.q.get()
|
||||||
debug.info(4,"Expanding: cost=" + str(cost) + " " + str(path))
|
debug.info(2,"Expanding: cost=" + str(cost) + " " + str(path))
|
||||||
|
|
||||||
# expand the last element
|
# expand the last element
|
||||||
neighbors = self.expand_dirs(path)
|
neighbors = self.expand_dirs(path)
|
||||||
|
|
@ -125,15 +136,19 @@ class grid:
|
||||||
|
|
||||||
for n in neighbors:
|
for n in neighbors:
|
||||||
newpath = path + [n]
|
newpath = path + [n]
|
||||||
|
if n not in self.map.keys():
|
||||||
|
self.map[n]=cell()
|
||||||
self.map[n].visited=True
|
self.map[n].visited=True
|
||||||
|
|
||||||
# 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):
|
||||||
return newpath
|
return (newpath,self.cost(newpath))
|
||||||
else:
|
else:
|
||||||
# path cost + predicted cost
|
# potential path cost + predicted cost
|
||||||
cost = self.cost(newpath) + self.cost_to_target(n)
|
cost = self.cost(newpath) + self.cost_to_target(n)
|
||||||
self.q.put((cost,newpath))
|
# only add the cost if it is less than our bound
|
||||||
|
if (cost < cost_bound):
|
||||||
|
self.q.put((cost,newpath))
|
||||||
|
|
||||||
debug.error("Unable to route path. Expand area?",-1)
|
debug.error("Unable to route path. Expand area?",-1)
|
||||||
|
|
||||||
|
|
@ -146,42 +161,54 @@ class grid:
|
||||||
def expand_dirs(self,path):
|
def expand_dirs(self,path):
|
||||||
"""
|
"""
|
||||||
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. Always follow horizontal/vertical
|
but not expanding to blocked cells. Expands in all directions
|
||||||
routing layer requirements. Extend in the future if not routable?
|
regardless of preferred directions.
|
||||||
Future: Do we want to allow non-preferred direction routing?
|
|
||||||
"""
|
"""
|
||||||
# expand from the last point
|
# expand from the last point
|
||||||
point = path[-1]
|
point = path[-1]
|
||||||
|
|
||||||
neighbors = []
|
neighbors = []
|
||||||
# check z layer for enforced direction routing
|
|
||||||
if point.z==0:
|
|
||||||
east = point + vector3d(1,0,0)
|
|
||||||
west= point + vector3d(-1,0,0)
|
|
||||||
if east.x<self.width and not self.map[east].blocked and not east in path:
|
|
||||||
neighbors.append(east)
|
|
||||||
if west.x>=0 and not self.map[west].blocked and not west in path:
|
|
||||||
neighbors.append(west)
|
|
||||||
|
|
||||||
up = point + vector3d(0,0,1)
|
east = point + vector3d(1,0,0)
|
||||||
if not self.map[up].blocked and not up in path:
|
self.add_map(east)
|
||||||
|
if not self.map[east].blocked and not east in path:
|
||||||
|
neighbors.append(east)
|
||||||
|
|
||||||
|
west= point + vector3d(-1,0,0)
|
||||||
|
self.add_map(west)
|
||||||
|
if not self.map[west].blocked and not west in path:
|
||||||
|
neighbors.append(west)
|
||||||
|
|
||||||
|
up = point + vector3d(0,0,1)
|
||||||
|
self.add_map(up)
|
||||||
|
if up.z<2 and not self.map[up].blocked and not up in path:
|
||||||
neighbors.append(up)
|
neighbors.append(up)
|
||||||
elif point.z==1:
|
|
||||||
north = point + vector3d(0,1,0)
|
|
||||||
south = point + vector3d(0,-1,0)
|
|
||||||
if north.y<self.height and not self.map[north].blocked and not north in path:
|
|
||||||
neighbors.append(north)
|
|
||||||
if south.y>=0 and not self.map[south].blocked and not south in path:
|
|
||||||
neighbors.append(south)
|
|
||||||
|
|
||||||
down = point + vector3d(0,0,-1)
|
north = point + vector3d(0,1,0)
|
||||||
if not self.map[down].blocked and not down in path:
|
self.add_map(north)
|
||||||
neighbors.append(down)
|
if not self.map[north].blocked and not north in path:
|
||||||
|
neighbors.append(north)
|
||||||
|
|
||||||
|
south = point + vector3d(0,-1,0)
|
||||||
|
self.add_map(south)
|
||||||
|
if not self.map[south].blocked and not south in path:
|
||||||
|
neighbors.append(south)
|
||||||
|
|
||||||
|
down = point + vector3d(0,0,-1)
|
||||||
|
self.add_map(down)
|
||||||
|
if down.z>=0 and not self.map[down].blocked and not down in path:
|
||||||
|
neighbors.append(down)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return neighbors
|
return neighbors
|
||||||
|
|
||||||
|
def add_map(self,p):
|
||||||
|
"""
|
||||||
|
Add a point to the map if it doesn't exist.
|
||||||
|
"""
|
||||||
|
if p not in self.map.keys():
|
||||||
|
self.map[p]=cell()
|
||||||
|
|
||||||
def init_queue(self):
|
def init_queue(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -190,7 +217,7 @@ class grid:
|
||||||
We will use an A* search, so this cost must be pessimistic.
|
We will use an A* search, so this cost must be pessimistic.
|
||||||
Cost so far will be the length of the path.
|
Cost so far will be the length of the path.
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Initializing queue.")
|
debug.info(4,"Initializing queue.")
|
||||||
for s in self.source:
|
for s in self.source:
|
||||||
cost = self.cost_to_target(s)
|
cost = self.cost_to_target(s)
|
||||||
debug.info(4,"Init: cost=" + str(cost) + " " + str([s]))
|
debug.info(4,"Init: cost=" + str(cost) + " " + str([s]))
|
||||||
|
|
@ -205,18 +232,52 @@ class grid:
|
||||||
cost = min(source.hpwl(t),cost)
|
cost = min(source.hpwl(t),cost)
|
||||||
return cost
|
return cost
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def cost(self,path):
|
def cost(self,path):
|
||||||
"""
|
"""
|
||||||
The cost of the path is the length plus a penalty for the number
|
The cost of the path is the length plus a penalty for the number
|
||||||
of vias.
|
of vias.
|
||||||
Future: Do we want to allow non-preferred direction routing?
|
We assume that non-preferred direction is penalized 2x.
|
||||||
"""
|
"""
|
||||||
prev_layer = path[0].z
|
# Ignore the source pin layer change, FIXME?
|
||||||
via_cost = 0
|
def pairwise(iterable):
|
||||||
for p in path:
|
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
||||||
if p.z != prev_layer:
|
a, b = tee(iterable)
|
||||||
via_cost += 2 # we count a via as 2x a wire track
|
next(b, None)
|
||||||
prev_layer = p.z
|
return zip(a, b)
|
||||||
|
|
||||||
return len(path)+via_cost
|
plist = pairwise(path)
|
||||||
|
cost = 0
|
||||||
|
for p0,p1 in plist:
|
||||||
|
if p0.z != p1.z: # via
|
||||||
|
cost += 2
|
||||||
|
elif p0.x != p1.x: # horizontal
|
||||||
|
cost += 2 if (p0.z == 1) else 1
|
||||||
|
elif p0.y != p1.y: # vertical
|
||||||
|
cost += 2 if (p0.z == 0) else 1
|
||||||
|
else:
|
||||||
|
debug.error("Non-changing direction!")
|
||||||
|
|
||||||
|
# for p in path:
|
||||||
|
# if p.z != prev_p.z:
|
||||||
|
# via_cost += 2 # we count a via as 2x a wire track
|
||||||
|
# prev_layer = p.z
|
||||||
|
# prev_p = p
|
||||||
|
#
|
||||||
|
#return len(path)+via_cost
|
||||||
|
return cost
|
||||||
|
|
||||||
|
def get_inertia(self,p0,p1):
|
||||||
|
"""
|
||||||
|
Sets the direction based on the previous direction we came from.
|
||||||
|
"""
|
||||||
|
# direction (index) of movement
|
||||||
|
if p0.x==p1.x:
|
||||||
|
return 1
|
||||||
|
elif p0.y==p1.y:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
# z direction
|
||||||
|
return 2
|
||||||
|
|
|
||||||
|
|
@ -154,15 +154,15 @@ class router:
|
||||||
|
|
||||||
self.find_blockages()
|
self.find_blockages()
|
||||||
|
|
||||||
self.rg.view("preroute.png")
|
#self.rg.view("preroute.png")
|
||||||
|
|
||||||
# returns the path in tracks
|
# returns the path in tracks
|
||||||
self.path = self.rg.route()
|
(self.path,cost) = self.rg.route()
|
||||||
debug.info(1,"Found path. ")
|
debug.info(1,"Found path: cost={0} ".format(cost))
|
||||||
debug.info(2,str(self.path))
|
debug.info(2,str(self.path))
|
||||||
self.set_path(self.path)
|
self.set_path(self.path)
|
||||||
|
|
||||||
self.rg.view("postroute.png")
|
#self.rg.view("postroute.png")
|
||||||
return
|
return
|
||||||
|
|
||||||
def add_route(self,cell):
|
def add_route(self,cell):
|
||||||
|
|
@ -170,18 +170,19 @@ class router:
|
||||||
Add the current wire route to the given design instance.
|
Add the current wire route to the given design instance.
|
||||||
"""
|
"""
|
||||||
# First, simplify the path for
|
# First, simplify the path for
|
||||||
|
debug.info(1,str(self.path))
|
||||||
contracted_path = self.contract_path(self.path)
|
contracted_path = self.contract_path(self.path)
|
||||||
debug.info(1,str(contracted_path))
|
debug.info(1,str(contracted_path))
|
||||||
|
|
||||||
# Make sure there's a pin enclosure on the source and dest
|
# Make sure there's a pin enclosure on the source and dest
|
||||||
src_shape = self.convert_track_to_shape(contracted_path[0])
|
src_shape = self.convert_track_to_shape(contracted_path[0])
|
||||||
cell.add_rect(layer=self.layers[0],
|
cell.add_rect(layer=self.layers[contracted_path[0].z],
|
||||||
offset=src_shape[0],
|
offset=src_shape[0],
|
||||||
width=src_shape[1].x-src_shape[0].x,
|
width=src_shape[1].x-src_shape[0].x,
|
||||||
height=src_shape[1].y-src_shape[0].y)
|
height=src_shape[1].y-src_shape[0].y)
|
||||||
|
|
||||||
dest_shape = self.convert_track_to_shape(contracted_path[-1])
|
dest_shape = self.convert_track_to_shape(contracted_path[-1])
|
||||||
cell.add_rect(layer=self.layers[0],
|
cell.add_rect(layer=self.layers[contracted_path[-1].z],
|
||||||
offset=dest_shape[0],
|
offset=dest_shape[0],
|
||||||
width=dest_shape[1].x-dest_shape[0].x,
|
width=dest_shape[1].x-dest_shape[0].x,
|
||||||
height=dest_shape[1].y-dest_shape[0].y)
|
height=dest_shape[1].y-dest_shape[0].y)
|
||||||
|
|
@ -189,8 +190,22 @@ class router:
|
||||||
|
|
||||||
# convert the path back to absolute units from tracks
|
# convert the path back to absolute units from tracks
|
||||||
abs_path = map(self.convert_point_to_units,contracted_path)
|
abs_path = map(self.convert_point_to_units,contracted_path)
|
||||||
debug.info(1,str(abs_path))
|
|
||||||
cell.add_wire(self.layers,abs_path)
|
cell.add_wire(self.layers,abs_path)
|
||||||
|
debug.info(1,str(abs_path))
|
||||||
|
|
||||||
|
# Check if a via is needed at the start point
|
||||||
|
if (contracted_path[0].z!=contracted_path[1].z):
|
||||||
|
# offset this by 1/2 the via size
|
||||||
|
c=contact(self.layers, (1, 1))
|
||||||
|
via_offset = vector(-0.5*c.width,-0.5*c.height)
|
||||||
|
cell.add_via(self.layers,abs_path[0]+via_offset)
|
||||||
|
# Check if a via is needed at the end point
|
||||||
|
if (contracted_path[-1].z!=contracted_path[-2].z):
|
||||||
|
# offset this by 1/2 the via size
|
||||||
|
c=contact(self.layers, (1, 1))
|
||||||
|
via_offset = vector(-0.5*c.width,-0.5*c.height)
|
||||||
|
cell.add_via(self.layers,abs_path[-1]+via_offset)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def create_steiner_routes(self,pins):
|
def create_steiner_routes(self,pins):
|
||||||
|
|
@ -245,10 +260,10 @@ class router:
|
||||||
Sets the direction based on the previous direction we came from.
|
Sets the direction based on the previous direction we came from.
|
||||||
"""
|
"""
|
||||||
# direction (index) of movement
|
# direction (index) of movement
|
||||||
if p0.x==p1.x:
|
if p0.x!=p1.x:
|
||||||
return 1
|
|
||||||
elif p0.y==p1.y:
|
|
||||||
return 0
|
return 0
|
||||||
|
elif p0.y!=p1.y:
|
||||||
|
return 1
|
||||||
else:
|
else:
|
||||||
# z direction
|
# z direction
|
||||||
return 2
|
return 2
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ class no_blockages_test(unittest.TestCase):
|
||||||
layer_stack =("metal1","via1","metal2")
|
layer_stack =("metal1","via1","metal2")
|
||||||
r.route(layer_stack,src="A",dest="B")
|
r.route(layer_stack,src="A",dest="B")
|
||||||
r.add_route(self)
|
r.add_route(self)
|
||||||
|
self.gds_write("mytemp.gds")
|
||||||
|
|
||||||
|
|
||||||
r = routing("test1", "01_no_blockages_test")
|
r = routing("test1", "01_no_blockages_test")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue