Fix unit during gds read. Fix blockage and pin rounding bugs.

This commit is contained in:
Matt Guthaus 2016-11-17 11:24:17 -08:00
parent 614ff23e3a
commit aa950c3b21
10 changed files with 165 additions and 92 deletions

View File

@ -678,7 +678,6 @@ class VlsiLayout:
pin_boundary=self.readPinInStructureList(label_coordinate, label_layer)
debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary))
pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],
pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
return [label_name, label_layer, pin_boundary]

View File

@ -256,7 +256,7 @@ class layout:
if gds_file == None:
gds_file = self.gds_file
debug.info(3, "Printing %s" % gds_file)
arrayCellLayout = gdsMill.VlsiLayout()
arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1)
reader.loadFromFile(gds_file)

View File

@ -6,26 +6,41 @@ class cell:
visited, etc.
"""
def __init__(self):
self.visited = False
self.path = False
self.blocked = False
self.source = False
self.target = False
def get_color(self):
r=g=b=0
count=0
# Blues are horizontal
if self.blocked:
return ImageColor.getrgb("Green")
# Reds are source/sink
if self.source or self.target:
return ImageColor.getrgb("Red")
[r1,g1,b1] = ImageColor.getrgb("Green")
r+=r1
g+=g1
b+=b1
count+=1
if self.source or self.target:
[r1,g1,b1] = ImageColor.getrgb("Red")
r+=r1
g+=g1
b+=b1
count+=1
if self.path:
return ImageColor.getrgb("Blue")
[r1,g1,b1] = ImageColor.getrgb("Blue")
r+=r1
g+=g1
b+=b1
count+=1
return [255,255,255]
if count>0:
return [int(r/count),int(g/count),int(b/count)]
else:
return [255,255,255]

View File

@ -34,14 +34,14 @@ class grid:
def view(self,):
def view(self):
"""
View the data by creating an RGB array and mapping the data
structure to the RGB color palette.
"""
v_map = np.zeros((self.width,self.height,3), 'uint8')
mid_map = np.ones((25,self.height,3), 'uint8')
mid_map = np.ones((25,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
@ -50,11 +50,17 @@ class grid:
for y in range(self.height):
h_map[x,y] = self.map[vector3d(x,y,0)].get_color()
v_map[x,y] = self.map[vector3d(x,y,1)].get_color()
# This is just for scale
if x==0 and y==0:
h_map[x,y] = [0,0,0]
v_map[x,y] = [0,0,0]
v_img = Image.fromarray(v_map, 'RGB').rotate(90)
mid_img = Image.fromarray(mid_map, 'RGB').rotate(90)
#v_img.show()
mid_img = Image.fromarray(mid_map, 'RGB').rotate(90)
h_img = Image.fromarray(h_map, 'RGB').rotate(90)
#h_img.show()
# concatenate them into a plot with the two layers
img = Image.new('RGB', (2*self.width+25, self.height))
img.paste(h_img, (0,0))
@ -64,13 +70,19 @@ class grid:
img.save("test.png")
def set_property(self,ll,ur,z,name,value=True):
for x in range(int(ll[0]),int(ur[0])):
for y in range(int(ll[1]),int(ur[1])):
assert(ur[1] >= ll[1] and ur[0] >= ll[0])
assert(ll[0]<self.width and ll[0]>=0)
assert(ll[1]<self.height and ll[1]>=0)
assert(ur[0]<self.width and ur[0]>=0)
assert(ur[1]<self.height and ur[1]>=0)
for x in range(int(ll[0]),int(ur[0])+1):
for y in range(int(ll[1]),int(ur[1])+1):
debug.info(3," Adding {3} x={0} y={1} z={2}".format(str(ll),str(ur),z,name))
setattr (self.map[vector3d(x,y,z)], name, True)
getattr (self, name).append(vector3d(x,y,z))
def add_blockage(self,ll,ur,z):
debug.info(1,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
debug.info(2,"Adding blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z))
self.set_property(ll,ur,z,"blocked")
def set_source(self,ll,ur,z):
@ -106,14 +118,17 @@ class grid:
# Keep expanding and adding to the priority queue until we are done
while not self.q.empty():
(cost,path) = self.q.get()
debug.info(2,"Expanding: cost=" + str(cost) + " " + str(path))
debug.info(2,"Expanding: cost=" + str(cost))
debug.info(3,str(path))
# expand the last element
neighbors = self.expand_dirs(path[-1])
neighbors = self.expand_dirs(path)
debug.info(2,"Neighbors: " + str(neighbors))
for n in neighbors:
newpath = path + [n]
self.map[n].visited=True
# check if we hit the target and are done
if self.is_target(n):
return newpath
@ -122,6 +137,7 @@ class grid:
cost = len(newpath) + self.cost_to_target(n)
self.q.put((cost,newpath))
self.view()
debug.error("Unable to route path. Expand area?",-1)
def is_target(self,point):
@ -130,34 +146,37 @@ class grid:
"""
return point in self.target
def expand_dirs(self,point):
def expand_dirs(self,path):
"""
Expand each of the four cardinal directions plus up or down
but not expanding to blocked cells. Always follow horizontal/vertical
routing layer requirements. Extend in the future if not routable?
"""
# expand from the last point
point = path[-1]
neighbors = []
# check z layer for enforced direction routing
if point.z==0:
east = point + vector3d(1,0,0)
west= point + vector3d(-11,0,0)
if east.x<self.width and not self.map[east].blocked:
if east.x<self.width and not self.map[east].blocked and not self.map[east].visited:
neighbors.append(east)
if west.x>=0 and not self.map[west].blocked:
if west.x>=0 and not self.map[west].blocked and not self.map[west].visited:
neighbors.append(west)
up = point + vector3d(0,0,1)
if not self.map[up].blocked:
if not self.map[up].blocked and not self.map[up].visited:
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:
if north.y<self.height and not self.map[north].blocked and not self.map[north].visited:
neighbors.append(north)
if south.y>=0 and not self.map[south].blocked:
if south.y>=0 and not self.map[south].blocked and not self.map[south].visited:
neighbors.append(south)
down = point + vector3d(0,0,-1)
if not self.map[down].blocked:
if not self.map[down].blocked and not self.map[down].visited:
neighbors.append(down)

View File

@ -20,34 +20,21 @@ class router:
"""
self.gds_name = gds_name
self.layout = gdsMill.VlsiLayout()
self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gds_name)
self.top_name = self.layout.rootStructureName
self.unit = float(self.layout.info['units'][0])
print "Units:",self.unit
self.pin_names = []
self.pin_shapes = {}
self.pin_layers = {}
self.boundary = self.layout.measureBoundary(self.top_name)
#print "Boundary: ",self.boundary
self.ll = vector(self.boundary[0])
self.ur = vector(self.boundary[1])
self.size = self.ur - self.ll
self.width = self.size.x
self.height = self.size.y
print "Boundary: ",self.boundary
print "Size: ", self.width,self.height
# to scale coordinates by units
self.unit_factor = [self.unit] * 2
# We will offset so ll is at (0,0)
self.offset = self.ll
print "Offset: ",self.offset
def set_top(self,top_name):
""" If we want to route something besides the top-level cell."""
@ -82,9 +69,20 @@ class router:
def create_routing_grid(self):
""" Create a routing grid that spans given area. Wires cannot exist outside region. """
# We will add a halo around the boundary
# of this many tracks
track_halo = 2
# We will offset so ll is at (-track_halo*track_width,-track_halo*track_width)
track_width_offset = vector([track_halo*self.track_width]*2)
self.offset = self.ll - track_width_offset
print "Offset: ",self.offset
width = self.size.x
height = self.size.y
print "Size: ", width,height
self.width_in_tracks = int(math.ceil(self.width/self.track_width))
self.height_in_tracks = int(math.ceil(self.height/self.track_width))
# pad the tracks on each side by the halo as well
self.width_in_tracks = int(math.ceil(width/self.track_width)) + 2*track_halo
self.height_in_tracks = int(math.ceil(height/self.track_width)) + 2*track_halo
print "Size (in tracks): ", self.width_in_tracks, self.height_in_tracks
@ -94,8 +92,12 @@ class router:
def find_pin(self,pin):
""" Finds the offsets to the gds pins """
(pin_name,pin_layer,pin_shape) = self.layout.readPin(str(pin))
debug.info(3,"Find pin {0} layer {1} shape {2}".format(pin_name,str(pin_layer),str(pin_shape)))
# repack the shape as a pair of vectors rather than four values
new_shape = self.convert_to_tracks([vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])])
shape=[vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])]
print shape
new_shape = self.convert_to_tracks(shape,round_bigger=False)
print new_shape
self.pin_names.append(pin_name)
self.pin_shapes[str(pin)] = new_shape
self.pin_layers[str(pin)] = pin_layer
@ -109,10 +111,16 @@ class router:
self.write_obstacle(self.top_name)
def route(self):
def route(self,layers,src, dest):
self.set_layers(layers)
self.create_routing_grid()
self.set_source(src)
self.set_target(dest)
self.find_blockages()
path = self.rg.route()
debug.info(0,"Found path: " + str(path))
self.rg.set_path(path)
debug.info(0,"Found path. ")
debug.info(2,str(path))
return path
def add_route(self,start, end, layerstack):
""" Add a wire route from the start to the end point"""
@ -136,13 +144,21 @@ class router:
coordinate += [(x, y)]
return coordinate
def min_max_coord(self, coordTrans):
def convert_shape_to_units(self, shape):
""" Scale a shape (two vector list) to user units """
unit_factor = [tech.GDS["unit"][0]] * 2
ll=shape[0].scale(unit_factor)
ur=shape[1].scale(unit_factor)
return [ll,ur]
def min_max_coord(self, coord):
"""Find the lowest and highest corner of a Rectangle"""
coordinate = []
minx = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
maxx = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0])
miny = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
maxy = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1])
minx = min(coord[0][0], coord[1][0], coord[2][0], coord[3][0])
maxx = max(coord[0][0], coord[1][0], coord[2][0], coord[3][0])
miny = min(coord[0][1], coord[1][1], coord[2][1], coord[3][1])
maxy = max(coord[0][1], coord[1][1], coord[2][1], coord[3][1])
coordinate += [vector(minx, miny)]
coordinate += [vector(maxx, maxy)]
return coordinate
@ -150,49 +166,45 @@ class router:
def set_source(self,name):
shape = self.find_pin(name)
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
debug.info(0,"Set source: " + str(name) + " " + str(shape) + " z=" + str(zindex))
debug.info(1,"Set source: " + str(name) + " " + str(shape) + " z=" + str(zindex))
self.rg.set_source(shape[0],shape[1],zindex)
def set_target(self,name):
shape = self.find_pin(name)
zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1
debug.info(0,"Set target: " + str(name) + " " + str(shape) + " z=" + str(zindex))
debug.info(1,"Set target: " + str(name) + " " + str(shape) + " z=" + str(zindex))
self.rg.set_target(shape[0],shape[1],zindex)
def write_obstacle(self, sref, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)):
"""Recursive write boundaries on each Structure in GDS file to LEF"""
for boundary in self.layout.structures[sref].boundaries:
coordTrans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift)
shape = self.min_max_coord(coordTrans)
coord_trans = self.translate_coordinates(boundary.coordinates, mirr, angle, xyShift)
shape_coords = self.min_max_coord(coord_trans)
shape = self.convert_shape_to_units(shape_coords)
if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]:
ll_microns=shape[0].scale(self.unit_factor)
ur_microns=shape[1].scale(self.unit_factor)
shape_tracks=self.convert_to_tracks([ll_microns,ur_microns])
# We round the pins down, so we must do this to skip them
pin_shape_tracks=self.convert_to_tracks(shape,round_bigger=False)
# don't add a blockage if this shape was a pin shape
if shape_tracks not in self.pin_shapes.values():
if pin_shape_tracks not in self.pin_shapes.values():
# inflate the ll and ur by 1 track in each direction
[ll,ur]=shape_tracks
ll = vector(0,0).max(ll + vector(-1,-1))
ur = vector(self.width_in_tracks-1,self.height_in_tracks-1).min(ur + vector(1,1))
[ll,ur]=self.convert_to_tracks(shape)
zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1
debug.info(1,"Blockage: "+str([ll,ur])+" z="+str(zlayer))
self.rg.add_blockage(ll,ur,zlayer)
else:
debug.info(2,"Skip: "+str(shape_tracks))
debug.info(2,"Skip: "+str(pin_shape_tracks))
# recurse given the mirror, angle, etc.
for cur_sref in self.layout.structures[sref].srefs:
sMirr = 1
if sref.transFlags[0] == True:
if cur_sref.transFlags[0] == True:
sMirr = -1
sAngle = math.radians(float(0))
if sref.rotateAngle:
if cur_sref.rotateAngle:
sAngle = math.radians(float(cur_sref.rotateAngle))
sAngle += angle
x = cur_sref.coordinates[0]
@ -201,23 +213,27 @@ class router:
newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1]
sxyShift = (newX, newY)
self.write_obstacle(cur_sref.sName, layer,sMirr, sAngle, sxyShift)
self.write_obstacle(cur_sref.sName, sMirr, sAngle, sxyShift)
def convert_to_tracks(self,shape):
def convert_to_tracks(self,shape,round_bigger=True):
"""
Convert a rectangular shape into track units.
"""
[ll,ur] = shape
# offset lowest corner object to to (0,0)
# offset lowest corner object to to (-track halo,-track halo)
ll = snap_to_grid(ll-self.offset)
ur = snap_to_grid(ur-self.offset)
# always round down, because we will add a track
# to inflate each obstacle object later.
# whereas pins should be conservative
ll = ll.scale(self.track_factor).ceil()
ur = ur.scale(self.track_factor).floor()
# Always round blockage shapes up.
if round_bigger:
ll = ll.scale(self.track_factor).floor()
ur = ur.scale(self.track_factor).ceil()
# Always round pin shapes down
else:
ll = ll.scale(self.track_factor).round()
ur = ur.scale(self.track_factor).round()
return [ll,ur]

View File

@ -9,7 +9,6 @@ sys.path.append(os.path.join(sys.path[0],".."))
import globals
import debug
import calibre
import vector
class no_blockages_test(unittest.TestCase):
@ -17,18 +16,24 @@ class no_blockages_test(unittest.TestCase):
globals.init_openram("config_{0}".format(OPTS.tech_name))
import router
import wire
import tech
#r=router.router("A_to_B_no_blockages.gds")
r=router.router("A_to_B_m1m2_blockages.gds")
#r=router.router("A_to_B_m1m2_blockages.gds")
#r=router.router("A_to_B_m1m2_same_layer_pins.gds")
r=router.router("A_to_B_m1m2_diff_layer_pins.gds")
layer_stack =("metal1","via1","metal2")
path=r.route(layer_stack,src="A",dest="B")
r.set_layers(("metal1","via1","metal2"))
r.create_routing_grid()
r.set_source("A")
r.set_target("B")
r.find_blockages()
r.route()
# For debug, to view the result as an image
r.rg.set_path(path)
r.rg.view()
OPTS.check_lvsdrc = False
#w = wire.wire(layer_stack, path)
OPTS.check_lvsdrc = True
#self.local_check(w)
#drc_errors = calibre.run_drc(name, gds_name)
drc_errors = 1
@ -39,6 +44,11 @@ class no_blockages_test(unittest.TestCase):
def local_check(self, w):
tempgds = OPTS.openram_temp + "temp.gds"
w.gds_write(tempgds)
self.assertFalse(calibre.run_drc(w.name, tempgds))
os.remove(tempgds)

View File

@ -21,12 +21,19 @@ def snap_to_grid(offset):
return out_offset
def gdsPinToOffset(gdsPin):
def gds_pin_center(gdsPin):
"""
This returns the center of a pin shape
"""
boundary = gdsPin[2]
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
def auto_measure_libcell(pin_list, name, units, layer):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.
Return these as a set of properties including the cell width/height too.
"""
cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi)
@ -39,7 +46,7 @@ def auto_measure_libcell(pin_list, name, units, layer):
[cell["width"], cell["height"]] = measure_result
for pin in pin_list:
cell[str(pin)] = gdsPinToOffset(cell_vlsi.readPin(str(pin)))
cell[str(pin)] = gds_pin_center(cell_vlsi.readPin(str(pin)))
return cell

View File

@ -23,11 +23,11 @@ class vector():
def __str__(self):
""" override print function output """
return "vector:["+str(self.x)+", "+str(self.y)+"]"
return "["+str(self.x)+","+str(self.y)+"]"
def __repr__(self):
""" override print function output """
return "["+str(self.x)+", "+str(self.y)+"]"
return "["+str(self.x)+","+str(self.y)+"]"
def __setitem__(self, index, value):
"""
@ -113,6 +113,13 @@ class vector():
"""
return vector(int(math.ceil(self.x)),int(math.ceil(self.y)))
def round(self):
"""
Override round function
"""
return vector(int(round(self.x)),int(round(self.y)))
def __eq__(self, other):
"""Override the default Equals behavior"""
if isinstance(other, self.__class__):