From aa950c3b21c578a58d8588dec403067d91bd9879 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 17 Nov 2016 11:24:17 -0800 Subject: [PATCH] Fix unit during gds read. Fix blockage and pin rounding bugs. --- compiler/gdsMill/gdsMill/vlsiLayout.py | 1 - compiler/hierarchy_layout.py | 2 +- compiler/router/cell.py | 33 +++-- compiler/router/grid.py | 51 +++++--- compiler/router/router.py | 118 ++++++++++-------- compiler/router/tests/01_no_blockages_test.py | 30 +++-- .../tests/A_to_B_m1m2_diff_layer_pins.gds | Bin 4096 -> 4096 bytes .../tests/A_to_B_m1m2_same_layer_pins.gds | Bin 4096 -> 4096 bytes compiler/utils.py | 11 +- compiler/vector.py | 11 +- 10 files changed, 165 insertions(+), 92 deletions(-) diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index a7e24cac..a64c562a 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -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] diff --git a/compiler/hierarchy_layout.py b/compiler/hierarchy_layout.py index 8b136b47..2abd591c 100644 --- a/compiler/hierarchy_layout.py +++ b/compiler/hierarchy_layout.py @@ -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) diff --git a/compiler/router/cell.py b/compiler/router/cell.py index e2bcca9c..c36c7957 100644 --- a/compiler/router/cell.py +++ b/compiler/router/cell.py @@ -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] diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 45b90d13..d0e17e52 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -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]=0) + assert(ll[1]=0) + assert(ur[0]=0) + assert(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=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=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) diff --git a/compiler/router/router.py b/compiler/router/router.py index 2f56772b..47ba2872 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -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] diff --git a/compiler/router/tests/01_no_blockages_test.py b/compiler/router/tests/01_no_blockages_test.py index 17d79660..89c3019e 100644 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -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) diff --git a/compiler/router/tests/A_to_B_m1m2_diff_layer_pins.gds b/compiler/router/tests/A_to_B_m1m2_diff_layer_pins.gds index 18a158b2f190c8cfa6cf7e8887fdcd71808c7a77..ebea35d149dca5c90c9cad11e37f1f307a6c60b9 100644 GIT binary patch delta 167 zcmZorXi$h_U}IncVi`sz1_=gk27U$}AU0(X0I~%c7#Tzu7#Mh%*iur{5=(PR7#KvD zS?v^#%wGC9_?x9tjQ9oLwPFl1tT^>fjC(BzGlq+etvs_RHQr>h6pNgo0Q3L<|CksU lxY!t2B!O6PV!r6+JjUBhK*Jcg_$K?YSTZiy+`;0(4FFFa9P|JH delta 149 zcmZorXi$h_U}IncVi`szh8hNL27U$}AU0(X0JB9HBpLV^c$nByQqvMkb4nN(M3`Ca z6pqYZ`Z)NTrBaOe1>dy_3^J@t3=-J&PKPx# diff --git a/compiler/router/tests/A_to_B_m1m2_same_layer_pins.gds b/compiler/router/tests/A_to_B_m1m2_same_layer_pins.gds index 05ec293639c4c15ddf684267946aee53322d6c75..19a31fbdde7bfe76bb22a2b23e0ea579c3f89557 100644 GIT binary patch delta 127 zcmZorXi$h_U}IncVi`sz1_=gk27U$}AU0(X0I~%cgcw8__!xMY*iur{5=(PR7#KvD zS?v^#%wGC9_?x9tjQ9oLwPFl1thn^@PmFub4l#y*a{!|k^W+#toy}~lzqlrGIsgEe C^%Xe) delta 170 zcmZorXi$h_U}IncVi`szh8hNL27U$}AU0(X0JB9HL>SZdy_3^J@t3=-J&PK