From 0766db9e111682c7f0aa3487cd05171d625f7b4f Mon Sep 17 00:00:00 2001 From: Matthew Guthaus Date: Wed, 12 Apr 2017 10:59:04 -0700 Subject: [PATCH] Rename unit test files according to test. Modify off-grid pins and blockages. Reorganize router code a bit. --- compiler/gdsMill/gdsMill/vlsiLayout.py | 10 +- compiler/router/grid.py | 13 +- compiler/router/router.py | 252 +++++++++++------- compiler/router/tests/01_no_blockages_test.py | 16 +- ...CD_two_nets.sp => 01_no_blockages_test.sp} | 0 ...AB_blockages.gds => 02_blockages_test.gds} | Bin .../{AB_blockages.sp => 02_blockages_test.sp} | 0 ...r_pins.gds => 03_same_layer_pins_test.gds} | Bin ...yer_pins.sp => 03_same_layer_pins_test.sp} | 0 ...r_pins.gds => 04_diff_layer_pins_test.gds} | Bin ...lockages.sp => 04_diff_layer_pins_test.sp} | 0 ...ABCD_two_nets.gds => 05_two_nets_test.gds} | Bin ...same_layer_pins.sp => 05_two_nets_test.sp} | 0 compiler/router/tests/AB_no_blockages.gds | Bin 2048 -> 2048 bytes compiler/utils.py | 2 +- 15 files changed, 166 insertions(+), 127 deletions(-) rename compiler/router/tests/{ABCD_two_nets.sp => 01_no_blockages_test.sp} (100%) rename compiler/router/tests/{AB_blockages.gds => 02_blockages_test.gds} (100%) rename compiler/router/tests/{AB_blockages.sp => 02_blockages_test.sp} (100%) rename compiler/router/tests/{AB_same_layer_pins.gds => 03_same_layer_pins_test.gds} (100%) rename compiler/router/tests/{AB_diff_layer_pins.sp => 03_same_layer_pins_test.sp} (100%) rename compiler/router/tests/{AB_diff_layer_pins.gds => 04_diff_layer_pins_test.gds} (100%) rename compiler/router/tests/{AB_no_blockages.sp => 04_diff_layer_pins_test.sp} (100%) rename compiler/router/tests/{ABCD_two_nets.gds => 05_two_nets_test.gds} (100%) rename compiler/router/tests/{AB_same_layer_pins.sp => 05_two_nets_test.sp} (100%) diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 22069855..4f498b04 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -650,7 +650,7 @@ class VlsiLayout: cellBoundary[3]=right_top_Y return cellBoundary - def readPin(self,label_name): + def readPinShape(self,label_name): """ Search for a pin label and return the largest enclosing rectangle on the same layer as the pin label. @@ -663,7 +663,7 @@ class VlsiLayout: label_layer = Text.drawingLayer label_coordinate = Text.coordinates - pin_boundaries=self.readAllPinInStructureList(label_coordinate, label_layer) + pin_boundaries=self.readAllPinShapesInStructureList(label_coordinate, label_layer) # sort the boundaries, return the max area pin boundary pin_boundaries.sort(cmpBoundaryAreas,reverse=True) @@ -675,7 +675,7 @@ class VlsiLayout: return [label_name, label_layer, pin_boundary] - def readAllPin(self,label_name): + def readAllPinShapes(self,label_name): """ Search for a pin label and return ALL the enclosing rectangles on the same layer as the pin label. @@ -688,7 +688,7 @@ class VlsiLayout: label_layer = Text.drawingLayer label_coordinate = Text.coordinates - pin_boundaries=self.readAllPinInStructureList(label_coordinate, label_layer) + pin_boundaries=self.readAllPinShapesInStructureList(label_coordinate, label_layer) # Convert to user units new_boundaries = [] @@ -699,7 +699,7 @@ class VlsiLayout: return [label_name, label_layer, new_boundaries] - def readAllPinInStructureList(self,label_coordinates,layer): + def readAllPinShapesInStructureList(self,label_coordinates,layer): """ Given the label coordinate, search for enclosing structures on the given layer. Return the single biggest area rectangle. diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 8f3e54cd..5f1f3557 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -34,14 +34,14 @@ class grid: - def view(self): + def view(self,filename="test.png"): """ 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((10,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 @@ -62,12 +62,12 @@ class grid: #h_img.show() # concatenate them into a plot with the two layers - img = Image.new('RGB', (2*self.width+25, self.height)) + img = Image.new('RGB', (2*self.width+10, self.height)) img.paste(h_img, (0,0)) img.paste(mid_img, (self.width,0)) - img.paste(v_img, (self.width+25,0)) - img.show() - img.save("test.png") + img.paste(v_img, (self.width+10,0)) + #img.show() + img.save(filename) def set_property(self,ll,ur,z,name,value=True): assert(ur[1] >= ll[1] and ur[0] >= ll[0]) @@ -135,7 +135,6 @@ class grid: cost = self.cost(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): diff --git a/compiler/router/router.py b/compiler/router/router.py index 9fc3b218..3fc701da 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -11,29 +11,33 @@ import grid class router: """A router class to read an obstruction map from a gds and plan a route on a given layer. This is limited to two layer routes. - """ + def __init__(self, gds_name): """Use the gds file for the blockages with the top module topName and layers for the layers to route on - """ + # Load the gds file and read in all the shapes self.gds_name = gds_name 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 + # A list of pin names for source and dest self.pin_names = [] + # The map of pin names to list of all pin shapes for a pin. self.pin_shapes = {} - # Used to track which shapes should not become blockages - self.all_pin_shapes = [] + # The corresponding layers of the above pin shapes self.pin_layers = {} - + # Used to track which shapes should not become blockages. This + # will contain all of both source and dest pin shapes in units not tracks. + self.all_pin_shapes = [] + + # The boundary will determine the limits to the size of the routing grid self.boundary = self.layout.measureBoundary(self.top_name) self.ll = vector(self.boundary[0]) self.ur = vector(self.boundary[1]) - self.size = self.ur - self.ll def set_top(self,top_name): @@ -53,7 +57,7 @@ class router: self.horiz_layer_width = tech.drc["minwidth_{0}".format(horiz_layer)] self.horiz_layer_number = tech.layer[horiz_layer] - # contacted track spacing + # Contacted track spacing. via_connect = contact(self.layers, (1, 1)) max_via_size = max(via_connect.width,via_connect.height) horiz_layer_spacing = tech.drc[str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name)] @@ -61,39 +65,42 @@ class router: self.horiz_track_width = max_via_size + horiz_layer_spacing self.vert_track_width = max_via_size + vert_layer_spacing - # This is so we can use a single resolution grid for both layers + # We'll keep horizontal and vertical tracks the same for simplicity. self.track_width = max(self.horiz_track_width,self.vert_track_width) - debug.info(1,"Track width:"+str(self.track_width)) + debug.info(1,"Track width: "+str(self.track_width)) + + self.track_widths = [self.track_width] * 2 + self.track_factor = [1/self.track_width] * 2 + debug.info(1,"Track factor: {0}".format(self.track_factor)) def create_routing_grid(self): - """ Create a routing grid that spans given area. Wires cannot exist outside region. """ + """ + 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 = 5 - debug.info(1,"Size: {0} x {1}".format(self.size.x,self.size.y)) + size = self.ur - self.ll + debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) - # pad the tracks on each side by the halo as well - self.left_in_tracks = int(math.floor(self.ll.x/self.track_width)) - track_halo - self.bottom_in_tracks = int(math.floor(self.ll.y/self.track_width)) - track_halo - self.right_in_tracks = int(math.ceil(self.ur.x/self.track_width)) + track_halo - self.top_in_tracks = int(math.ceil(self.ur.y/self.track_width)) + track_halo + # The routing grid starts at the self.ll and goes up/right + # The +1 is because the source/dest object may get expanded outside the region + self.height_in_tracks = int(math.ceil(self.ur.x/self.track_width))+2 + self.width_in_tracks = int(math.ceil(self.ur.y/self.track_width))+2 - # We will offset so th lower left is track 0,0 - self.track_offset = vector(-self.left_in_tracks,-self.bottom_in_tracks) - - self.width_in_tracks = self.right_in_tracks - self.left_in_tracks - self.height_in_tracks = self.top_in_tracks - self.bottom_in_tracks - - debug.info(1,"Size (in tracks): {0} x {1}".format(self.width_in_tracks, self.height_in_tracks)) + debug.info(1,"Size (in tracks, from ll): {0} x {1}".format(self.width_in_tracks, self.height_in_tracks)) self.rg = grid.grid(self.height_in_tracks,self.width_in_tracks) def find_pin(self,pin): - """ Finds the pin shapes and converts to tracks """ - (pin_name,pin_layer,pin_shapes) = self.layout.readAllPin(str(pin)) + """ + Finds the pin shapes and converts to tracks + """ + + # Returns all the shapes that enclose a pin on a given layer + (pin_name,pin_layer,pin_shapes) = self.layout.readAllPinShapes(str(pin)) self.pin_shapes[str(pin)]=[] self.pin_names.append(pin_name) @@ -102,21 +109,26 @@ class router: debug.info(2,"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 shape=[vector(pin_shape[0],pin_shape[1]),vector(pin_shape[2],pin_shape[3])] - new_shape = self.convert_shape_to_tracks(shape,round_bigger=False) - self.pin_shapes[str(pin)].append(new_shape) - self.all_pin_shapes.append(new_shape) + # convert the pin coordinates to tracks and round the sizes down + self.pin_shapes[str(pin)].append(shape) self.pin_layers[str(pin)] = pin_layer + self.all_pin_shapes.append(shape) return self.pin_shapes[str(pin)] def find_blockages(self): + """ + Iterate through all the layers and write the obstacles to the routing grid. + """ if len(self.pin_names)!=2: debug.error("Must set pins before creating blockages.",-1) - for layer in self.layers: self.write_obstacle(self.top_name) def clear_pins(self): + """ + Reset the source and destination pins to start a new routing. + """ self.source = [] self.dest = [] @@ -125,42 +137,79 @@ class router: Route a single source-destination net and return the simplified rectilinear path. """ + # Clear the pins if we have previously routed self.clear_pins() + + # Set up layers and track sizes self.set_layers(layers) + + # Creat a routing grid over the entire area + # FIXME: This could be created only over the routing region, + # but this is simplest for now. self.create_routing_grid() + self.set_source(src) + self.set_target(dest) self.find_blockages() + + self.rg.view("preroute.png") + # returns the path in tracks - path = self.rg.route() + self.path = self.rg.route() debug.info(1,"Found path. ") - debug.info(2,str(path)) - self.set_path(path) - # First, simplify the path. - contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) + debug.info(2,str(self.path)) + self.set_path(self.path) + + self.rg.view("postroute.png") + return + + def add_route(self,cell): + """ + Add the current wire route to the given design instance. + """ + # First, simplify the path for + contracted_path = self.contract_path(self.path) + debug.info(1,str(contracted_path)) + + # Make sure there's a pin enclosure on the source and dest + src_shape = self.convert_track_to_shape(contracted_path[0]) + cell.add_rect(layer=self.layers[0], + offset=src_shape[0], + width=src_shape[1].x-src_shape[0].x, + height=src_shape[1].y-src_shape[0].y) + + dest_shape = self.convert_track_to_shape(contracted_path[-1]) + cell.add_rect(layer=self.layers[0], + offset=dest_shape[0], + width=dest_shape[1].x-dest_shape[0].x, + height=dest_shape[1].y-dest_shape[0].y) + + # convert the path back to absolute units from tracks abs_path = map(self.convert_point_to_units,contracted_path) debug.info(1,str(abs_path)) - - # Make sure there's a pin enclosure on the source and dest - src_shape = self.convert_track_to_shape(contracted_path[0]) - dest_shape = self.convert_track_to_shape(contracted_path[-1]) + cell.add_wire(self.layers,abs_path) - return (src_shape,abs_path,dest_shape) def create_steiner_routes(self,pins): - """Find a set of steiner points and then return the list of - point-to-point routes.""" + """ + Find a set of steiner points and then return the list of + point-to-point routes. + """ pass def find_steiner_points(self,pins): - """ Find the set of steiner points and return them.""" + """ + Find the set of steiner points and return them. + """ pass def translate_coordinates(self, coord, mirr, angle, xyShift): - """Calculate coordinates after flip, rotate, and shift""" + """ + Calculate coordinates after flip, rotate, and shift + """ coordinate = [] for item in coord: x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0]) @@ -169,7 +218,9 @@ class router: return coordinate def convert_shape_to_units(self, shape): - """ Scale a shape (two vector list) to user units """ + """ + 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) @@ -177,7 +228,9 @@ class router: def min_max_coord(self, coord): - """Find the lowest and highest corner of a Rectangle""" + """ + Find the lowest and highest corner of a Rectangle + """ coordinate = [] 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]) @@ -188,71 +241,81 @@ class router: return coordinate 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: - inertia = 1 + return 1 elif p0.y==p1.y: - inertia = 0 + return 0 else: - inertia = 2 - return inertia + # z direction + return 2 + def contract_path(self,path): """ - Remove intermediate points in a rectilinear path. + Remove intermediate points in a rectilinear path. """ newpath = [path[0]] - for i in range(len(path)-1): - if i==0: - continue + 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]) - else: - continue + # always add the last path newpath.append(path[-1]) - return newpath def set_path(self,path): + """ + Mark the path in the routing grid. + """ debug.info(3,"Set path: " + str(path)) self.rg.set_path(path) def set_source(self,name): + """ + Mark the grids that are in the pin rectangle ranges to have the source property. + """ shapes = self.find_pin(name) zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1 for shape in shapes: - debug.info(1,"Set source: " + str(name) + " " + str(shape) + " z=" + str(zindex)) - self.rg.set_source(shape[0],shape[1],zindex) + shape_in_tracks=self.convert_shape_to_tracks(shape) + debug.info(1,"Set source: " + str(name) + " " + str(shape_in_tracks) + " z=" + str(zindex)) + self.rg.set_source(shape_in_tracks[0],shape_in_tracks[1],zindex) def set_target(self,name): + """ + Mark the grids that are in the pin rectangle ranges to have the target property. + """ shapes = self.find_pin(name) zindex = 0 if self.pin_layers[name]==self.horiz_layer_number else 1 for shape in shapes: - debug.info(1,"Set target: " + str(name) + " " + str(shape) + " z=" + str(zindex)) - self.rg.set_target(shape[0],shape[1],zindex) + shape_in_tracks=self.convert_shape_to_tracks(shape) + debug.info(1,"Set target: " + str(name) + " " + str(shape_in_tracks) + " z=" + str(zindex)) + self.rg.set_target(shape_in_tracks[0],shape_in_tracks[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""" - + """ + Recursive write boundaries as blockages to the routing grid. + Recurses for each Structure in GDS. + """ for boundary in self.layout.structures[sref].boundaries: 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) + # only consider the two layers that we are routing on if boundary.drawingLayer in [self.vert_layer_number,self.horiz_layer_number]: - # We round the pins down, so we must do this to skip them - pin_shape_tracks=self.convert_units_to_tracks(shape,round_bigger=False) - + zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1 # don't add a blockage if this shape was a pin shape - if pin_shape_tracks not in self.all_pin_shapes: - # inflate the ll and ur by 1 track in each direction - [ll,ur]=self.convert_units_to_tracks(shape) - zlayer = 0 if boundary.drawingLayer==self.horiz_layer_number else 1 + if shape not in self.all_pin_shapes: + [ll,ur]=self.convert_shape_to_tracks(shape) self.rg.add_blockage(ll,ur,zlayer) @@ -277,55 +340,42 @@ class router: """ Convert a path set of tracks to center line path. """ - track_factor = [self.track_width] * 2 # we can ignore the layers here # add_wire will filter out duplicates pt = vector(p[0],p[1]) - pt=pt.scale(track_factor) - return snap_to_grid(pt) + pt=pt.scale(self.track_widths) + return pt - def convert_units_to_tracks(self,shape,round_bigger=True): + def convert_shape_to_tracks(self,shape,round_bigger=False): """ Convert a rectangular shape into track units. """ [ll,ur] = shape - - # offset lowest corner object to to (-track halo,-track halo) ll = snap_to_grid(ll) ur = snap_to_grid(ur) # to scale coordinates to tracks - track_factor = [1/self.track_width] * 2 - - - - if round_bigger: # Always round blockage shapes up. - ll = ll.scale(track_factor).floor() + self.track_offset - ur = ur.scale(track_factor).ceil() + self.track_offset - if ll.x<0: - ll.x=0 - if ll.y<0: - ll.y=0 - else: # Always round pin shapes down - ll = ll.scale(track_factor).round() - ur = ur.scale(track_factor).round() - + debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) + ll=ll.scale(self.track_factor) + ur=ur.scale(self.track_factor) + ll = ll.floor() if round_bigger else ll.round() + ur = ur.ceil() if round_bigger else ur.round() + #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) return [ll,ur] - def convert_track_to_shape(self,tracks): + def convert_track_to_shape(self,track): """ - Convert a set of track units into a rectangle shape. + Convert a grid point into a rectangle shape that occupies the centered + track. """ - - tracks = tracks - self.track_offset # to scale coordinates to tracks - # FIXME: should be offset by spacing, not track width - x = tracks.x*self.track_width - 0.25*self.track_width - y = tracks.y*self.track_width - 0.25*self.track_width + # FIXME: should be the metal width no the track width? + x = track.x*self.track_width - 0.5*self.track_width + y = track.y*self.track_width - 0.5*self.track_width # offset lowest corner object to to (-track halo,-track halo) ll = snap_to_grid(vector(x,y)) - ur = snap_to_grid(ll + vector(0.5*self.track_width,0.5*self.track_width)) + ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) return [ll,ur] diff --git a/compiler/router/tests/01_no_blockages_test.py b/compiler/router/tests/01_no_blockages_test.py index 900c3d33..b4524c48 100644 --- a/compiler/router/tests/01_no_blockages_test.py +++ b/compiler/router/tests/01_no_blockages_test.py @@ -49,21 +49,11 @@ class no_blockages_test(unittest.TestCase): self.gdsname = "{0}/{1}.gds".format(os.path.dirname(os.path.realpath(__file__)),gdsname) r=router.router(self.gdsname) layer_stack =("metal1","via1","metal2") - (src_rect,path,dest_rect)=r.route(layer_stack,src="A",dest="B") - #r.rg.view() - self.add_rect(layer=layer_stack[0], - offset=src_rect[0], - width=src_rect[1].x-src_rect[0].x, - height=src_rect[1].y-src_rect[0].y) - self.add_wire(layer_stack,path) - self.add_rect(layer=layer_stack[0], - offset=dest_rect[0], - width=dest_rect[1].x-dest_rect[0].x, - height=dest_rect[1].y-dest_rect[0].y) - + r.route(layer_stack,src="A",dest="B") + r.add_route(self) - r = routing("test1", "AB_no_blockages") + r = routing("test1", "01_no_blockages_test") self.local_check(r) # fails if there are any DRC errors on any cells diff --git a/compiler/router/tests/ABCD_two_nets.sp b/compiler/router/tests/01_no_blockages_test.sp similarity index 100% rename from compiler/router/tests/ABCD_two_nets.sp rename to compiler/router/tests/01_no_blockages_test.sp diff --git a/compiler/router/tests/AB_blockages.gds b/compiler/router/tests/02_blockages_test.gds similarity index 100% rename from compiler/router/tests/AB_blockages.gds rename to compiler/router/tests/02_blockages_test.gds diff --git a/compiler/router/tests/AB_blockages.sp b/compiler/router/tests/02_blockages_test.sp similarity index 100% rename from compiler/router/tests/AB_blockages.sp rename to compiler/router/tests/02_blockages_test.sp diff --git a/compiler/router/tests/AB_same_layer_pins.gds b/compiler/router/tests/03_same_layer_pins_test.gds similarity index 100% rename from compiler/router/tests/AB_same_layer_pins.gds rename to compiler/router/tests/03_same_layer_pins_test.gds diff --git a/compiler/router/tests/AB_diff_layer_pins.sp b/compiler/router/tests/03_same_layer_pins_test.sp similarity index 100% rename from compiler/router/tests/AB_diff_layer_pins.sp rename to compiler/router/tests/03_same_layer_pins_test.sp diff --git a/compiler/router/tests/AB_diff_layer_pins.gds b/compiler/router/tests/04_diff_layer_pins_test.gds similarity index 100% rename from compiler/router/tests/AB_diff_layer_pins.gds rename to compiler/router/tests/04_diff_layer_pins_test.gds diff --git a/compiler/router/tests/AB_no_blockages.sp b/compiler/router/tests/04_diff_layer_pins_test.sp similarity index 100% rename from compiler/router/tests/AB_no_blockages.sp rename to compiler/router/tests/04_diff_layer_pins_test.sp diff --git a/compiler/router/tests/ABCD_two_nets.gds b/compiler/router/tests/05_two_nets_test.gds similarity index 100% rename from compiler/router/tests/ABCD_two_nets.gds rename to compiler/router/tests/05_two_nets_test.gds diff --git a/compiler/router/tests/AB_same_layer_pins.sp b/compiler/router/tests/05_two_nets_test.sp similarity index 100% rename from compiler/router/tests/AB_same_layer_pins.sp rename to compiler/router/tests/05_two_nets_test.sp diff --git a/compiler/router/tests/AB_no_blockages.gds b/compiler/router/tests/AB_no_blockages.gds index 471153ac36bd590d26b31294bbcdd8362834a134..299444f338d7e0d2d42803e9ec36505eff81ffea 100644 GIT binary patch delta 187 zcmZn=Xb_kn9xBMd!obBK${@v{&cMUOmXey5SejG9z#ziRYUkK#?tN_e8%viR`U_@9 zeqfMcWnvIu;AY@w5CN)ZVnEi*$Htaxn3R*BoSm4SI&qEO8X=57@a2O=}eAbRGY-8GjWZ>#5_Az Sb_NC(mdzg-`Pn8hIROAZ_!{>B diff --git a/compiler/utils.py b/compiler/utils.py index 2e400843..7149ed85 100644 --- a/compiler/utils.py +++ b/compiler/utils.py @@ -45,7 +45,7 @@ def auto_measure_libcell(pin_list, name, units, layer): [cell["width"], cell["height"]] = measure_result for pin in pin_list: - cell[str(pin)] = gds_pin_center(cell_vlsi.readPin(str(pin))) + cell[str(pin)] = gds_pin_center(cell_vlsi.readPinShape(str(pin))) return cell