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) pin_boundary=self.readPinInStructureList(label_coordinate, label_layer)
debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary)) 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=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],
pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]] pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
return [label_name, label_layer, pin_boundary] return [label_name, label_layer, pin_boundary]

View File

@ -256,7 +256,7 @@ class layout:
if gds_file == None: if gds_file == None:
gds_file = self.gds_file gds_file = self.gds_file
debug.info(3, "Printing %s" % 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 = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1)
reader.loadFromFile(gds_file) reader.loadFromFile(gds_file)

View File

@ -6,26 +6,41 @@ class cell:
visited, etc. visited, etc.
""" """
def __init__(self): def __init__(self):
self.visited = False
self.path = False self.path = False
self.blocked = False self.blocked = False
self.source = False self.source = False
self.target = False self.target = False
def get_color(self): def get_color(self):
r=g=b=0
count=0
# Blues are horizontal # Blues are horizontal
if self.blocked: if self.blocked:
return ImageColor.getrgb("Green") [r1,g1,b1] = ImageColor.getrgb("Green")
# Reds are source/sink r+=r1
if self.source or self.target: g+=g1
return ImageColor.getrgb("Red") 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: 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 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((25,self.height,3), 'uint8') mid_map = np.ones((25,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
@ -50,11 +50,17 @@ class grid:
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
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) 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 = Image.fromarray(h_map, 'RGB').rotate(90)
#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+25, self.height)) img = Image.new('RGB', (2*self.width+25, self.height))
img.paste(h_img, (0,0)) img.paste(h_img, (0,0))
@ -64,13 +70,19 @@ class grid:
img.save("test.png") img.save("test.png")
def set_property(self,ll,ur,z,name,value=True): def set_property(self,ll,ur,z,name,value=True):
for x in range(int(ll[0]),int(ur[0])): assert(ur[1] >= ll[1] and ur[0] >= ll[0])
for y in range(int(ll[1]),int(ur[1])): 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) setattr (self.map[vector3d(x,y,z)], name, True)
getattr (self, name).append(vector3d(x,y,z)) getattr (self, name).append(vector3d(x,y,z))
def add_blockage(self,ll,ur,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") self.set_property(ll,ur,z,"blocked")
def set_source(self,ll,ur,z): 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 # 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(2,"Expanding: cost=" + str(cost) + " " + str(path)) debug.info(2,"Expanding: cost=" + str(cost))
debug.info(3,str(path))
# expand the last element # expand the last element
neighbors = self.expand_dirs(path[-1]) neighbors = self.expand_dirs(path)
debug.info(2,"Neighbors: " + str(neighbors)) debug.info(2,"Neighbors: " + str(neighbors))
for n in neighbors: for n in neighbors:
newpath = path + [n] newpath = path + [n]
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
@ -122,6 +137,7 @@ class grid:
cost = len(newpath) + self.cost_to_target(n) cost = len(newpath) + self.cost_to_target(n)
self.q.put((cost,newpath)) self.q.put((cost,newpath))
self.view()
debug.error("Unable to route path. Expand area?",-1) debug.error("Unable to route path. Expand area?",-1)
def is_target(self,point): def is_target(self,point):
@ -130,34 +146,37 @@ class grid:
""" """
return point in self.target 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 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. Always follow horizontal/vertical
routing layer requirements. Extend in the future if not routable? routing layer requirements. Extend in the future if not routable?
""" """
# expand from the last point
point = path[-1]
neighbors = [] neighbors = []
# check z layer for enforced direction routing # check z layer for enforced direction routing
if point.z==0: if point.z==0:
east = point + vector3d(1,0,0) east = point + vector3d(1,0,0)
west= point + vector3d(-11,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) 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) neighbors.append(west)
up = point + vector3d(0,0,1) 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) neighbors.append(up)
elif point.z==1: elif point.z==1:
north = point + vector3d(0,1,0) north = point + vector3d(0,1,0)
south = 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) 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) neighbors.append(south)
down = point + vector3d(0,0,-1) 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) neighbors.append(down)

View File

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

View File

@ -9,7 +9,6 @@ sys.path.append(os.path.join(sys.path[0],".."))
import globals import globals
import debug import debug
import calibre import calibre
import vector
class no_blockages_test(unittest.TestCase): 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)) globals.init_openram("config_{0}".format(OPTS.tech_name))
import router import router
import wire
import tech
#r=router.router("A_to_B_no_blockages.gds") #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")) # For debug, to view the result as an image
r.rg.set_path(path)
r.create_routing_grid()
r.set_source("A")
r.set_target("B")
r.find_blockages()
r.route()
r.rg.view() 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 = calibre.run_drc(name, gds_name)
drc_errors = 1 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 return out_offset
def gdsPinToOffset(gdsPin): def gds_pin_center(gdsPin):
"""
This returns the center of a pin shape
"""
boundary = gdsPin[2] boundary = gdsPin[2]
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])] return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
def auto_measure_libcell(pin_list, name, units, layer): 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_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds"
cell_vlsi = gdsMill.VlsiLayout(units=units) cell_vlsi = gdsMill.VlsiLayout(units=units)
reader = gdsMill.Gds2reader(cell_vlsi) reader = gdsMill.Gds2reader(cell_vlsi)
@ -39,7 +46,7 @@ def auto_measure_libcell(pin_list, name, units, layer):
[cell["width"], cell["height"]] = measure_result [cell["width"], cell["height"]] = measure_result
for pin in pin_list: 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 return cell

View File

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