mirror of https://github.com/VLSIDA/OpenRAM.git
Improve supply routing for ring and side pins
This commit is contained in:
parent
da67edbde8
commit
f6587badad
|
|
@ -41,7 +41,8 @@ class layout():
|
||||||
|
|
||||||
self.width = None
|
self.width = None
|
||||||
self.height = None
|
self.height = None
|
||||||
self.bounding_box = None
|
self.bounding_box = None # The rectangle shape
|
||||||
|
self.bbox = None # The ll, ur coords
|
||||||
# Holds module/cell layout instances
|
# Holds module/cell layout instances
|
||||||
self.insts = []
|
self.insts = []
|
||||||
# Set of names to check for duplicates
|
# Set of names to check for duplicates
|
||||||
|
|
@ -1163,6 +1164,57 @@ class layout():
|
||||||
|
|
||||||
self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()]
|
self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()]
|
||||||
|
|
||||||
|
def get_bbox(self, side="all", big_margin=0, little_margin=0):
|
||||||
|
"""
|
||||||
|
Get the bounding box from the GDS
|
||||||
|
"""
|
||||||
|
gds_filename = OPTS.openram_temp + "temp.gds"
|
||||||
|
# If didn't specify a gds blockage file, write it out to read the gds
|
||||||
|
# This isn't efficient, but easy for now
|
||||||
|
# Load the gds file and read in all the shapes
|
||||||
|
self.gds_write(gds_filename)
|
||||||
|
layout = gdsMill.VlsiLayout(units=GDS["unit"])
|
||||||
|
reader = gdsMill.Gds2reader(layout)
|
||||||
|
reader.loadFromFile(gds_filename)
|
||||||
|
top_name = layout.rootStructureName
|
||||||
|
|
||||||
|
if not self.bbox:
|
||||||
|
# The boundary will determine the limits to the size
|
||||||
|
# of the routing grid
|
||||||
|
boundary = layout.measureBoundary(top_name)
|
||||||
|
# These must be un-indexed to get rid of the matrix type
|
||||||
|
ll = vector(boundary[0][0], boundary[0][1])
|
||||||
|
ur = vector(boundary[1][0], boundary[1][1])
|
||||||
|
else:
|
||||||
|
ll, ur = self.bbox
|
||||||
|
|
||||||
|
ll_offset = vector(0, 0)
|
||||||
|
ur_offset = vector(0, 0)
|
||||||
|
if side in ["ring", "top"]:
|
||||||
|
ur_offset += vector(0, big_margin)
|
||||||
|
else:
|
||||||
|
ur_offset += vector(0, little_margin)
|
||||||
|
if side in ["ring", "bottom"]:
|
||||||
|
ll_offset += vector(0, big_margin)
|
||||||
|
else:
|
||||||
|
ll_offset += vector(0, little_margin)
|
||||||
|
if side in ["ring", "left"]:
|
||||||
|
ll_offset += vector(big_margin, 0)
|
||||||
|
else:
|
||||||
|
ll_offset += vector(little_margin, 0)
|
||||||
|
if side in ["ring", "right"]:
|
||||||
|
ur_offset += vector(big_margin, 0)
|
||||||
|
else:
|
||||||
|
ur_offset += vector(little_margin, 0)
|
||||||
|
bbox = (ll - ll_offset, ur + ur_offset)
|
||||||
|
size = ur - ll
|
||||||
|
debug.info(1, "Size: {0} x {1} with perimeter big margin {2} little margin {3}".format(size.x,
|
||||||
|
size.y,
|
||||||
|
big_margin,
|
||||||
|
little_margin))
|
||||||
|
|
||||||
|
return bbox
|
||||||
|
|
||||||
def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None):
|
def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None):
|
||||||
"""
|
"""
|
||||||
Add a layer that surrounds the given instances. Useful
|
Add a layer that surrounds the given instances. Useful
|
||||||
|
|
|
||||||
|
|
@ -112,23 +112,25 @@ class lef:
|
||||||
for pin_name in self.pins:
|
for pin_name in self.pins:
|
||||||
pins = self.get_pins(pin_name)
|
pins = self.get_pins(pin_name)
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
inflated_pin = pin.inflated_pin(multiple=1)
|
inflated_pin = pin.inflated_pin(multiple=2)
|
||||||
another_iteration_needed = True
|
continue_fragmenting = True
|
||||||
while another_iteration_needed:
|
while continue_fragmenting:
|
||||||
another_iteration_needed = False
|
continue_fragmenting = False
|
||||||
old_blockages = list(self.blockages[pin.layer])
|
old_blockages = list(self.blockages[pin.layer])
|
||||||
for blockage in old_blockages:
|
for blockage in old_blockages:
|
||||||
if blockage.overlaps(inflated_pin):
|
if blockage.overlaps(inflated_pin):
|
||||||
intersection_shape = blockage.intersection(inflated_pin)
|
intersection_shape = blockage.intersection(inflated_pin)
|
||||||
# If it is zero area, don't add the pin
|
# If it is zero area, don't split the blockage
|
||||||
if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
|
if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
|
||||||
continue
|
continue
|
||||||
another_iteration_needed = True
|
|
||||||
# Remove the old blockage and add the new ones
|
# Remove the old blockage and add the new ones
|
||||||
self.blockages[pin.layer].remove(blockage)
|
self.blockages[pin.layer].remove(blockage)
|
||||||
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
|
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
|
||||||
new_blockages = blockage.cut(intersection_pin)
|
new_blockages = blockage.cut(intersection_pin)
|
||||||
self.blockages[pin.layer].extend(new_blockages)
|
self.blockages[pin.layer].extend(new_blockages)
|
||||||
|
# We split something so make another pass
|
||||||
|
continue_fragmenting = True
|
||||||
|
|
||||||
def lef_write_header(self):
|
def lef_write_header(self):
|
||||||
""" Header of LEF file """
|
""" Header of LEF file """
|
||||||
|
|
|
||||||
|
|
@ -606,7 +606,9 @@ class pin_layout:
|
||||||
# Don't add the existing shape in if it overlaps the pin shape
|
# Don't add the existing shape in if it overlaps the pin shape
|
||||||
if new_shape.contains(shape):
|
if new_shape.contains(shape):
|
||||||
continue
|
continue
|
||||||
new_shapes.append(new_shape)
|
# Only add non-zero shapes
|
||||||
|
if new_shape.area() > 0:
|
||||||
|
new_shapes.append(new_shape)
|
||||||
|
|
||||||
return new_shapes
|
return new_shapes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ nominal_corner_only = True
|
||||||
# Local wordlines have issues with met3 power routing for now
|
# Local wordlines have issues with met3 power routing for now
|
||||||
#local_array_size = 16
|
#local_array_size = 16
|
||||||
|
|
||||||
#route_supplies = "ring"
|
route_supplies = "ring"
|
||||||
route_supplies = "left"
|
#route_supplies = "left"
|
||||||
check_lvsdrc = True
|
check_lvsdrc = True
|
||||||
#perimeter_pins = False
|
#perimeter_pins = False
|
||||||
#netlist_only = True
|
#netlist_only = True
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ class grid:
|
||||||
# This is really lower left bottom layer and upper right top layer in 3D.
|
# This is really lower left bottom layer and upper right top layer in 3D.
|
||||||
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
|
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
|
||||||
self.ur = vector3d(ur.x, ur.y, 0).scale(self.track_factor).round()
|
self.ur = vector3d(ur.x, ur.y, 0).scale(self.track_factor).round()
|
||||||
|
debug.info(1, "BBOX coords: ll=" + str(ll) + " ur=" + str(ur))
|
||||||
|
debug.info(1, "BBOX grids: ll=" + str(self.ll) + " ur=" + str(self.ur))
|
||||||
|
|
||||||
# let's leave the map sparse, cells are created on demand to reduce memory
|
# let's leave the map sparse, cells are created on demand to reduce memory
|
||||||
self.map={}
|
self.map={}
|
||||||
|
|
@ -127,33 +129,47 @@ class grid:
|
||||||
Side specifies which side.
|
Side specifies which side.
|
||||||
Layer specifies horizontal (0) or vertical (1)
|
Layer specifies horizontal (0) or vertical (1)
|
||||||
Width specifies how wide the perimter "stripe" should be.
|
Width specifies how wide the perimter "stripe" should be.
|
||||||
|
Works from the inside out from the bbox (ll, ur)
|
||||||
"""
|
"""
|
||||||
|
if "ring" in side:
|
||||||
|
ring_width = width
|
||||||
|
else:
|
||||||
|
ring_width = 0
|
||||||
|
|
||||||
|
if "ring" in side:
|
||||||
|
ring_offset = offset
|
||||||
|
else:
|
||||||
|
ring_offset = 0
|
||||||
|
|
||||||
perimeter_list = []
|
perimeter_list = []
|
||||||
# Add the left/right columns
|
# Add the left/right columns
|
||||||
if side=="all" or side=="left":
|
if side=="all" or "left" in side:
|
||||||
for x in range(self.ll.x + offset, self.ll.x + width + offset, 1):
|
for x in range(self.ll.x - offset, self.ll.x - width - offset, -1):
|
||||||
for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1):
|
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
perimeter_list.append(vector3d(x, y, layer))
|
perimeter_list.append(vector3d(x, y, layer))
|
||||||
|
|
||||||
if side=="all" or side=="right":
|
if side=="all" or "right" in side:
|
||||||
for x in range(self.ur.x - width - offset, self.ur.x - offset, 1):
|
for x in range(self.ur.x + offset, self.ur.x + width + offset, 1):
|
||||||
for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1):
|
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
perimeter_list.append(vector3d(x, y, layer))
|
perimeter_list.append(vector3d(x, y, layer))
|
||||||
|
|
||||||
if side=="all" or side=="bottom":
|
if side=="all" or "bottom" in side:
|
||||||
for y in range(self.ll.y + offset, self.ll.y + width + offset, 1):
|
for y in range(self.ll.y - offset, self.ll.y - width - offset, -1):
|
||||||
for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1):
|
for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1):
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
perimeter_list.append(vector3d(x, y, layer))
|
perimeter_list.append(vector3d(x, y, layer))
|
||||||
|
|
||||||
if side=="all" or side=="top":
|
if side=="all" or "top" in side:
|
||||||
for y in range(self.ur.y - width - offset, self.ur.y - offset, 1):
|
for y in range(self.ur.y + offset, self.ur.y + width + offset, 1):
|
||||||
for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1):
|
for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1):
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
perimeter_list.append(vector3d(x, y, layer))
|
perimeter_list.append(vector3d(x, y, layer))
|
||||||
|
|
||||||
|
# Add them all to the map
|
||||||
|
self.add_map(perimeter_list)
|
||||||
|
|
||||||
return perimeter_list
|
return perimeter_list
|
||||||
|
|
||||||
def add_perimeter_target(self, side="all", layers=[0, 1]):
|
def add_perimeter_target(self, side="all", layers=[0, 1]):
|
||||||
|
|
|
||||||
|
|
@ -82,31 +82,13 @@ class router(router_tech):
|
||||||
"""
|
"""
|
||||||
Initialize the ll,ur values with the paramter or using the layout boundary.
|
Initialize the ll,ur values with the paramter or using the layout boundary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# If didn't specify a gds blockage file, write it out to read the gds
|
|
||||||
# This isn't efficient, but easy for now
|
|
||||||
# Load the gds file and read in all the shapes
|
|
||||||
self.cell.gds_write(self.gds_filename)
|
|
||||||
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
|
|
||||||
self.reader = gdsMill.Gds2reader(self.layout)
|
|
||||||
self.reader.loadFromFile(self.gds_filename)
|
|
||||||
self.top_name = self.layout.rootStructureName
|
|
||||||
|
|
||||||
if not bbox:
|
if not bbox:
|
||||||
# The boundary will determine the limits to the size
|
self.bbox = self.cell.get_bbox(margin)
|
||||||
# of the routing grid
|
|
||||||
self.boundary = self.layout.measureBoundary(self.top_name)
|
|
||||||
# These must be un-indexed to get rid of the matrix type
|
|
||||||
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
|
|
||||||
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
|
|
||||||
else:
|
else:
|
||||||
self.ll, self.ur = bbox
|
self.bbox = bbox
|
||||||
|
|
||||||
|
(self.ll, self.ur) = self.bbox
|
||||||
|
|
||||||
margin_offset = vector(margin, margin)
|
|
||||||
self.bbox = (self.ll - margin_offset, self.ur + margin_offset)
|
|
||||||
size = self.ur - self.ll
|
|
||||||
debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x, size.y, margin))
|
|
||||||
|
|
||||||
def get_bbox(self):
|
def get_bbox(self):
|
||||||
return self.bbox
|
return self.bbox
|
||||||
|
|
||||||
|
|
@ -893,19 +875,21 @@ class router(router_tech):
|
||||||
# Clearing the blockage of this pin requires the inflated pins
|
# Clearing the blockage of this pin requires the inflated pins
|
||||||
self.clear_blockages(pin_name)
|
self.clear_blockages(pin_name)
|
||||||
|
|
||||||
def add_side_supply_pin(self, name, side="left", width=2):
|
def add_side_supply_pin(self, name, side="left", width=3, space=2):
|
||||||
"""
|
"""
|
||||||
Adds a supply pin to the perimeter and resizes the bounding box.
|
Adds a supply pin to the perimeter and resizes the bounding box.
|
||||||
"""
|
"""
|
||||||
pg = pin_group(name, [], self)
|
pg = pin_group(name, [], self)
|
||||||
if name == "gnd":
|
# Offset two spaces inside and one between the rings
|
||||||
offset = width + 1
|
if name == "vdd":
|
||||||
|
offset = width + 2 * space
|
||||||
else:
|
else:
|
||||||
offset = 1
|
offset = space
|
||||||
if side in ["left", "right"]:
|
if side in ["left", "right"]:
|
||||||
layers = [1]
|
layers = [1]
|
||||||
else:
|
else:
|
||||||
layers = [0]
|
layers = [0]
|
||||||
|
|
||||||
pg.grids = set(self.rg.get_perimeter_list(side=side,
|
pg.grids = set(self.rg.get_perimeter_list(side=side,
|
||||||
width=width,
|
width=width,
|
||||||
margin=self.margin,
|
margin=self.margin,
|
||||||
|
|
@ -920,39 +904,39 @@ class router(router_tech):
|
||||||
|
|
||||||
self.new_pins[name] = pg.pins
|
self.new_pins[name] = pg.pins
|
||||||
|
|
||||||
def add_ring_supply_pin(self, name, width=2):
|
def add_ring_supply_pin(self, name, width=3, space=2):
|
||||||
"""
|
"""
|
||||||
Adds a ring supply pin that goes inside the given bbox.
|
Adds a ring supply pin that goes inside the given bbox.
|
||||||
"""
|
"""
|
||||||
pg = pin_group(name, [], self)
|
pg = pin_group(name, [], self)
|
||||||
# Offset the vdd inside one ring width
|
# Offset two spaces inside and one between the rings
|
||||||
# Units are in routing grids
|
# Units are in routing grids
|
||||||
if name == "gnd":
|
if name == "vdd":
|
||||||
offset = width + 1
|
offset = width + 2 * space
|
||||||
else:
|
else:
|
||||||
offset = 1
|
offset = space
|
||||||
|
|
||||||
# LEFT
|
# LEFT
|
||||||
left_grids = set(self.rg.get_perimeter_list(side="left",
|
left_grids = set(self.rg.get_perimeter_list(side="left_ring",
|
||||||
width=width,
|
width=width,
|
||||||
margin=self.margin,
|
margin=self.margin,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layers=[1]))
|
layers=[1]))
|
||||||
|
|
||||||
# RIGHT
|
# RIGHT
|
||||||
right_grids = set(self.rg.get_perimeter_list(side="right",
|
right_grids = set(self.rg.get_perimeter_list(side="right_ring",
|
||||||
width=width,
|
width=width,
|
||||||
margin=self.margin,
|
margin=self.margin,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layers=[1]))
|
layers=[1]))
|
||||||
# TOP
|
# TOP
|
||||||
top_grids = set(self.rg.get_perimeter_list(side="top",
|
top_grids = set(self.rg.get_perimeter_list(side="top_ring",
|
||||||
width=width,
|
width=width,
|
||||||
margin=self.margin,
|
margin=self.margin,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
layers=[0]))
|
layers=[0]))
|
||||||
# BOTTOM
|
# BOTTOM
|
||||||
bottom_grids = set(self.rg.get_perimeter_list(side="bottom",
|
bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring",
|
||||||
width=width,
|
width=width,
|
||||||
margin=self.margin,
|
margin=self.margin,
|
||||||
offset=offset,
|
offset=offset,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class supply_tree_router(router):
|
||||||
# The pin escape router already made the bounding box big enough,
|
# The pin escape router already made the bounding box big enough,
|
||||||
# so we can use the regular bbox here.
|
# so we can use the regular bbox here.
|
||||||
if pin_type:
|
if pin_type:
|
||||||
debug.check(pin_type in ["left", "right", "top", "bottom", "tree", "ring"],
|
debug.check(pin_type in ["left", "right", "top", "bottom", "single", "ring"],
|
||||||
"Invalid pin type {}".format(pin_type))
|
"Invalid pin type {}".format(pin_type))
|
||||||
self.pin_type = pin_type
|
self.pin_type = pin_type
|
||||||
router.__init__(self,
|
router.__init__(self,
|
||||||
|
|
|
||||||
|
|
@ -329,13 +329,21 @@ class sram_1bank(sram_base):
|
||||||
# Some technologies have an isolation
|
# Some technologies have an isolation
|
||||||
self.add_dnwell(inflate=2)
|
self.add_dnwell(inflate=2)
|
||||||
|
|
||||||
|
# We need the initial bbox for the supply rings later
|
||||||
|
# because the perimeter pins will change the bbox
|
||||||
# Route the pins to the perimeter
|
# Route the pins to the perimeter
|
||||||
|
pre_bbox = None
|
||||||
if OPTS.perimeter_pins:
|
if OPTS.perimeter_pins:
|
||||||
self.route_escape_pins()
|
pre_bbox = self.get_bbox(side="ring",
|
||||||
|
big_margin=self.m3_pitch)
|
||||||
|
bbox = self.get_bbox(side=OPTS.route_supplies,
|
||||||
|
big_margin=14 * self.m3_pitch,
|
||||||
|
little_margin=4 * self.m3_pitch)
|
||||||
|
self.route_escape_pins(bbox)
|
||||||
|
|
||||||
# Route the supplies first since the MST is not blockage aware
|
# Route the supplies first since the MST is not blockage aware
|
||||||
# and signals can route to anywhere on sides (it is flexible)
|
# and signals can route to anywhere on sides (it is flexible)
|
||||||
self.route_supplies()
|
self.route_supplies(pre_bbox)
|
||||||
|
|
||||||
def route_dffs(self, add_routes=True):
|
def route_dffs(self, add_routes=True):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ class sram_base(design, verilog, lef):
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
debug.error("Must override pure virtual function.", -1)
|
debug.error("Must override pure virtual function.", -1)
|
||||||
|
|
||||||
def route_supplies(self):
|
def route_supplies(self, bbox=None):
|
||||||
""" Route the supply grid and connect the pins to them. """
|
""" Route the supply grid and connect the pins to them. """
|
||||||
|
|
||||||
# Copy the pins to the top level
|
# Copy the pins to the top level
|
||||||
|
|
@ -252,11 +252,14 @@ class sram_base(design, verilog, lef):
|
||||||
return
|
return
|
||||||
elif OPTS.route_supplies == "grid":
|
elif OPTS.route_supplies == "grid":
|
||||||
from supply_grid_router import supply_grid_router as router
|
from supply_grid_router import supply_grid_router as router
|
||||||
rtr=router(grid_stack, self)
|
rtr=router(layers=grid_stack,
|
||||||
|
design=self,
|
||||||
|
bbox=bbox)
|
||||||
else:
|
else:
|
||||||
from supply_tree_router import supply_tree_router as router
|
from supply_tree_router import supply_tree_router as router
|
||||||
rtr=router(grid_stack,
|
rtr=router(layers=grid_stack,
|
||||||
self,
|
design=self,
|
||||||
|
bbox=bbox,
|
||||||
pin_type=OPTS.route_supplies)
|
pin_type=OPTS.route_supplies)
|
||||||
|
|
||||||
rtr.route()
|
rtr.route()
|
||||||
|
|
@ -283,7 +286,7 @@ class sram_base(design, verilog, lef):
|
||||||
pin.width(),
|
pin.width(),
|
||||||
pin.height())
|
pin.height())
|
||||||
|
|
||||||
elif OPTS.route_supplies:
|
elif OPTS.route_supplies or OPTS.route_supplies == "single":
|
||||||
# Update these as we may have routed outside the region (perimeter pins)
|
# Update these as we may have routed outside the region (perimeter pins)
|
||||||
lowest_coord = self.find_lowest_coords()
|
lowest_coord = self.find_lowest_coords()
|
||||||
|
|
||||||
|
|
@ -321,7 +324,7 @@ class sram_base(design, verilog, lef):
|
||||||
# Grid is left with many top level pins
|
# Grid is left with many top level pins
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def route_escape_pins(self):
|
def route_escape_pins(self, bbox):
|
||||||
"""
|
"""
|
||||||
Add the top-level pins for a single bank SRAM with control.
|
Add the top-level pins for a single bank SRAM with control.
|
||||||
"""
|
"""
|
||||||
|
|
@ -364,7 +367,7 @@ class sram_base(design, verilog, lef):
|
||||||
from signal_escape_router import signal_escape_router as router
|
from signal_escape_router import signal_escape_router as router
|
||||||
rtr=router(layers=self.m3_stack,
|
rtr=router(layers=self.m3_stack,
|
||||||
design=self,
|
design=self,
|
||||||
margin=8 * self.m3_pitch)
|
bbox=bbox)
|
||||||
rtr.escape_route(pins_to_route)
|
rtr.escape_route(pins_to_route)
|
||||||
|
|
||||||
def compute_bus_sizes(self):
|
def compute_bus_sizes(self):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue