mirror of https://github.com/VLSIDA/OpenRAM.git
Fix power ring routing boundary bug.
This commit is contained in:
parent
7e7670581c
commit
01a73b31e1
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]:
|
||||
|
|
|
|||
Loading…
Reference in New Issue