Fix power ring routing boundary bug.

This commit is contained in:
mrg 2022-03-18 10:32:25 -07:00
parent 7e7670581c
commit 01a73b31e1
10 changed files with 98 additions and 71 deletions

View File

@ -1322,7 +1322,7 @@ class layout():
self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()]
def get_bbox(self, side="all", big_margin=0, little_margin=0):
def get_bbox(self, side="all", margin=0):
"""
Get the bounding box from the GDS
"""
@ -1349,27 +1349,18 @@ class layout():
ll_offset = vector(0, 0)
ur_offset = vector(0, 0)
if side in ["ring", "top", "all"]:
ur_offset += vector(0, big_margin)
else:
ur_offset += vector(0, little_margin)
ur_offset += vector(0, margin)
if side in ["ring", "bottom", "all"]:
ll_offset += vector(0, big_margin)
else:
ll_offset += vector(0, little_margin)
ll_offset += vector(0, margin)
if side in ["ring", "left", "all"]:
ll_offset += vector(big_margin, 0)
else:
ll_offset += vector(little_margin, 0)
ll_offset += vector(margin, 0)
if side in ["ring", "right", "all"]:
ur_offset += vector(big_margin, 0)
else:
ur_offset += vector(little_margin, 0)
ur_offset += vector(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,
debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x,
size.y,
big_margin,
little_margin))
margin))
return bbox

View File

@ -177,15 +177,18 @@ class delay_chain(design.design):
# The routing to connect the loads is over the first and last cells
# We have an even number of drivers and must only do every other
# supply rail
if OPTS.experimental_power:
self.route_horizontal_pins("vdd")
self.route_horizontal_pins("gnd")
else:
for inst in self.driver_inst_list:
load_list = self.load_inst_map[inst]
for pin_name in ["vdd", "gnd"]:
pin = load_list[0].get_pin(pin_name)
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
for inst in self.driver_inst_list:
load_list = self.load_inst_map[inst]
for pin_name in ["vdd", "gnd"]:
pin = load_list[0].get_pin(pin_name)
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
pin = load_list[-2].get_pin(pin_name)
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
pin = load_list[-2].get_pin(pin_name)
self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0))
def add_layout_pins(self):

View File

@ -78,7 +78,7 @@ class wordline_driver_array(design.design):
"""
# Experiment with power straps
if OPTS.experimental_power:
if OPTS.tech_name=="sky130" or OPTS.experimental_power:
if layer_props.wordline_driver.vertical_supply:
self.route_vertical_pins("vdd", insts=self.wld_inst)
self.route_vertical_pins("gnd", insts=self.wld_inst)

View File

@ -68,6 +68,16 @@ class grid:
self.add_map(n)
return self.map[n].blocked
def is_inside(self, n):
if not isinstance(n, vector3d):
for item in n:
if self.is_inside(item):
return True
else:
return False
else:
return n.x >= self.ll.x and n.x <= self.ur.x and n.y >= self.ll.y and n.y <= self.ur.y
def set_path(self, n, value=True):
if isinstance(n, (list, tuple, set, frozenset)):
for item in n:
@ -128,7 +138,7 @@ class grid:
"""
Side specifies which side.
Layer specifies horizontal (0) or vertical (1)
Width specifies how wide the perimter "stripe" should be.
Width specifies how wide the perimeter "stripe" should be.
Works from the inside out from the bbox (ll, ur)
"""
if "ring" in side:

View File

@ -92,12 +92,19 @@ class router(router_tech):
def get_bbox(self):
return self.bbox
def create_routing_grid(self, router_type):
def create_routing_grid(self, router_type=None):
"""
Create a sprase routing grid with A* expansion functions.
Create (or recreate) a sprase routing grid with A* expansion functions.
"""
debug.check(router_type or hasattr(self, "router_type"), "Must specify a routing grid type.")
self.init_bbox(self.bbox, self.margin)
self.rg = router_type(self.ll, self.ur, self.track_width)
if router_type:
self.router_type = router_type
self.rg = router_type(self.ll, self.ur, self.track_width)
else:
self.rg = self.router_type(self.ll, self.ur, self.track_width)
def clear_pins(self):
"""
@ -927,40 +934,34 @@ class router(router_tech):
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 outside the given bbox.
"""
pg = pin_group(name, [], self)
# Offset two spaces inside and one between the rings
# Units are in routing grids
if name == "gnd":
offset = width + 2 * space
else:
offset = space
# LEFT
left_grids = set(self.rg.get_perimeter_list(side="left_ring",
width=width,
margin=self.margin,
offset=offset,
offset=space,
layers=[1]))
# RIGHT
right_grids = set(self.rg.get_perimeter_list(side="right_ring",
width=width,
margin=self.margin,
offset=offset,
offset=space,
layers=[1]))
# TOP
top_grids = set(self.rg.get_perimeter_list(side="top_ring",
width=width,
margin=self.margin,
offset=offset,
offset=space,
layers=[0]))
# BOTTOM
bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring",
width=width,
margin=self.margin,
offset=offset,
offset=space,
layers=[0]))
horizontal_layer_grids = left_grids | right_grids
@ -972,6 +973,7 @@ class router(router_tech):
# Add vias in the overlap points
horizontal_corner_grids = vertical_layer_grids & horizontal_layer_grids
corners = []
for g in horizontal_corner_grids:
self.add_via(g)
@ -984,6 +986,15 @@ class router(router_tech):
self.pin_groups[name].append(pg)
self.new_pins[name] = pg.pins
# Update the bbox so that it now includes the new pins
for p in pg.pins:
if p.lx() < self.ll.x or p.by() < self.ll.y:
self.ll = p.ll()
if p.rx() > self.ur.x or p.uy() > self.ur.y:
self.ur = p.ur()
self.bbox = (self.ll, self.ur)
self.create_routing_grid()
def get_new_pins(self, name):
return self.new_pins[name]
@ -1274,11 +1285,18 @@ class router(router_tech):
"""
debug.info(2, "Adding router info")
show_bbox = False
show_blockages = False
show_blockage_grids = False
show_enclosures = False
show_all_grids = True
if show_bbox:
self.cell.add_rect(layer="text",
offset=vector(self.ll.x, self.ll.y),
width=self.ur.x - self.ll.x,
height=self.ur.y - self.ll.y)
if show_all_grids:
for g in self.rg.map:
self.annotate_grid(g)

View File

@ -63,7 +63,7 @@ class signal_escape_router(router):
print_time("Maze routing pins",datetime.now(), start_time, 3)
# self.write_debug_gds("final_escape_router.gds",False)
#self.write_debug_gds("final_escape_router.gds",False)
return True

View File

@ -119,10 +119,11 @@ class signal_grid(grid):
# Expand all directions.
neighbors = curpath.expand_dirs()
# Filter the out of region ones
# Filter the blocked ones
unblocked_neighbors = [x for x in neighbors if not self.is_blocked(x)]
valid_neighbors = [x for x in neighbors if self.is_inside(x) and not self.is_blocked(x)]
return unblocked_neighbors
return valid_neighbors
def hpwl(self, src, dest):
"""

View File

@ -43,6 +43,7 @@ class supply_tree_router(router):
bbox=bbox,
route_track_width=self.route_track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"):
"""
Route the two nets in a single layer.
@ -75,6 +76,9 @@ class supply_tree_router(router):
self.add_ring_supply_pin(self.vdd_name)
self.add_ring_supply_pin(self.gnd_name)
#self.write_debug_gds("initial_tree_router.gds",False)
#breakpoint()
# Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter
start_time = datetime.now()
@ -82,8 +86,6 @@ class supply_tree_router(router):
self.route_pins(gnd_name)
print_time("Maze routing supplies", datetime.now(), start_time, 3)
# self.write_debug_gds("final_tree_router.gds",False)
# Did we route everything??
if not self.check_all_routed(vdd_name):
return False
@ -144,15 +146,15 @@ class supply_tree_router(router):
# Route MST components
for index, (src, dest) in enumerate(connections):
if not (index % 100):
if not (index % 25):
debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index))
self.route_signal(pin_name, src, dest)
# if pin_name == "gnd":
# print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
# print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))
# self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
if False and pin_name == "gnd":
print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))
self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
#self.write_debug_gds("final.gds", True)
#self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False)
#return
def route_signal(self, pin_name, src_idx, dest_idx):
@ -161,7 +163,7 @@ class supply_tree_router(router):
# Second pass, clear prior pin blockages so that you can route over other metal
# of the same supply. Otherwise, this can create a lot of circular routes due to accidental overlaps.
for unblock_routes in [False, True]:
for detour_scale in [5 * pow(2, x) for x in range(5)]:
for detour_scale in [2 * pow(2, x) for x in range(5)]:
debug.info(2, "Routing {0} to {1} with scale {2}".format(src_idx, dest_idx, detour_scale))
# Clear everything in the routing grid.
@ -187,6 +189,8 @@ class supply_tree_router(router):
# Actually run the A* router
if self.run_router(detour_scale=detour_scale):
return
if detour_scale > 2:
self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False)
self.write_debug_gds("debug_route.gds", True)

View File

@ -336,31 +336,27 @@ class sram_1bank(sram_base):
# Some technologies have an isolation
self.add_dnwell(inflate=2.5)
# Route the supplies together and/or to the ring/stripes.
# This is done with the original bbox since the escape routes need to
# be outside of the ring for OpenLane
rt = router_tech(self.supply_stack, 1)
init_bbox = self.get_bbox(side="ring",
margin=rt.track_width)
# We need the initial bbox for the supply rings later
# because the perimeter pins will change the bbox
# Route the pins to the perimeter
pre_bbox = None
if OPTS.perimeter_pins:
rt = router_tech(self.supply_stack, 1)
if OPTS.supply_pin_type in ["ring", "left", "right", "top", "bottom"]:
big_margin = 12 * rt.track_width
little_margin = 2 * rt.track_width
else:
big_margin = 6 * rt.track_width
little_margin = 0
pre_bbox = self.get_bbox(side="ring",
big_margin=rt.track_width)
bbox = self.get_bbox(side=OPTS.supply_pin_type,
big_margin=big_margin,
little_margin=little_margin)
# We now route the escape routes far enough out so that they will
# reach past the power ring or stripes on the sides
# The power rings are 4 tracks wide with 2 tracks spacing, so space it
# 11 tracks out
bbox = self.get_bbox(side="ring",
margin=11*rt.track_width)
self.route_escape_pins(bbox)
# Route the supplies first since the MST is not blockage aware
# and signals can route to anywhere on sides (it is flexible)
self.route_supplies(pre_bbox)
self.route_supplies(init_bbox)
def route_dffs(self, add_routes=True):

View File

@ -243,6 +243,7 @@ class sram_base(design, verilog, lef):
for inst in self.insts:
self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name])
# Pick the router type
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
@ -250,6 +251,7 @@ class sram_base(design, verilog, lef):
from supply_grid_router import supply_grid_router as router
else:
from supply_tree_router import supply_tree_router as router
rtr=router(layers=self.supply_stack,
design=self,
bbox=bbox,
@ -257,6 +259,8 @@ class sram_base(design, verilog, lef):
rtr.route()
# This removes the original pre-supply routing pins and replaces them
# with the ring or peripheral power pins
if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]: