From 01a73b31e18702357b7ee6c7da7d6f1e0533f576 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 18 Mar 2022 10:32:25 -0700 Subject: [PATCH] Fix power ring routing boundary bug. --- compiler/base/hierarchy_layout.py | 23 ++++-------- compiler/modules/delay_chain.py | 19 ++++++---- compiler/modules/wordline_driver_array.py | 2 +- compiler/router/grid.py | 12 +++++- compiler/router/router.py | 46 ++++++++++++++++------- compiler/router/signal_escape_router.py | 2 +- compiler/router/signal_grid.py | 5 ++- compiler/router/supply_tree_router.py | 22 ++++++----- compiler/sram/sram_1bank.py | 34 ++++++++--------- compiler/sram/sram_base.py | 4 ++ 10 files changed, 98 insertions(+), 71 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 3ebc4ac3..403920c6 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -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 diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index e044aaa3..a5bcea27 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -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): diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index a13f4bcd..b4c2c54f 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -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) diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 843fb3ed..a366ebe0 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -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: diff --git a/compiler/router/router.py b/compiler/router/router.py index f82a6128..2502d322 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -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) diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 7cc41d97..d727e900 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -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 diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 340db093..3d8c69eb 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -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): """ diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 88d02f2e..bb21813f 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -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) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index ce10eb4f..110b8330 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -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): diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index ec690610..f6782597 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -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"]: