diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index f4f4f2e4..47818d33 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -473,6 +473,17 @@ class layout(): """ self.pin_map = {} + def replace_layout_pin(self, text, pin): + """ + Remove the old pin and replace with a new one + """ + self.remove_layout_pin(text) + self.add_layout_pin(text=text, + layer=pin.layer, + offset=pin.ll(), + width=pin.width(), + height=pin.height()) + def add_layout_pin(self, text, layer, offset, width=None, height=None): """ Create a labeled pin diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py index 6232b411..15ec2fc8 100644 --- a/compiler/router/grid_path.py +++ b/compiler/router/grid_path.py @@ -5,13 +5,12 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import debug from vector3d import vector3d from itertools import tee from grid import grid -from grid_cell import grid_cell from direction import direction + class grid_path: """ A grid path is a list of lists of grid cells. @@ -29,8 +28,7 @@ class grid_path: self.pathlist = [] def __str__(self): - #import pprint - p = str(self.pathlist) #pprint.pformat(self.pathlist) + p = str(self.pathlist) if self.name != "": return (str(self.name) + " : " + p) return p @@ -188,12 +186,10 @@ class grid_path: return newwave - def set_layer(self, zindex): new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave] self.pathlist = new_pathlist - def overlap(self, other): """ Return the overlap waves ignoring different layers diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py index 30518793..a579ff4b 100644 --- a/compiler/router/grid_utils.py +++ b/compiler/router/grid_utils.py @@ -9,7 +9,6 @@ Some utility functions for sets of grid cells. """ -import debug import math from direction import direction from vector3d import vector3d @@ -44,6 +43,7 @@ def get_upper_right(curset): ur = p return ur + def get_lower_left(curset): ll = None for p in curset: @@ -51,7 +51,8 @@ def get_lower_left(curset): ll = p return ll -def get_border( curset, direct): + +def get_border(curset, direct): """ Return the furthest cell(s) in a given direction. """ @@ -86,6 +87,7 @@ def get_border( curset, direct): newset = set(maxc) return newset + def expand_border(curset, direct): """ Expand the current set of sells in a given direction. @@ -95,6 +97,7 @@ def expand_border(curset, direct): next_border_set = increment_set(border_set, direct) return next_border_set + def expand_borders(curset): """ Return the expansions in planar directions. @@ -106,6 +109,7 @@ def expand_borders(curset): return(north_set, east_set, south_set, west_set) + def inflate_cell(cell, distance): """ Expand the current cell in all directions and return the set. @@ -122,6 +126,7 @@ def inflate_cell(cell, distance): return newset + def inflate_set(curset, distance): """ Expand the set in all directions by the given number of grids. @@ -136,6 +141,7 @@ def inflate_set(curset, distance): # Recurse with less depth return inflate_set(newset,distance-1) + def flatten_set(curset): """ Flatten until we have a set of vector3d objects. @@ -149,7 +155,6 @@ def flatten_set(curset): return newset - def distance_set(coord, curset): """ Return the distance from a coordinate to any item in the set diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 5ba87dc9..474702a4 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -493,7 +493,6 @@ class pin_group: debug.error("Could not find a connector for {} with {}".format(self.pins, self.enclosures)) self.router.write_debug_gds("no_connector.gds") - import pdb; pdb.set_trace() self.enclosures.append(connector) # At this point, the pins are overlapping, diff --git a/compiler/router/router.py b/compiler/router/router.py index 09f691f7..7428b75c 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1022,11 +1022,10 @@ class router(router_tech): if path: debug.info(2, "Found path: cost={0} {1}".format(cost, str(path))) - self.paths.append(path) + self.paths.append(grid_utils.flatten_set(path)) self.add_route(path) - path_set = grid_utils.flatten_set(path) - self.path_blockages.append(path_set) + self.path_blockages.append(self.paths[-1]) return True else: return False @@ -1133,7 +1132,7 @@ class router(router_tech): show_all_grids = True if show_all_grids: - self.rg.add_all_grids() + #self.rg.add_all_grids() for g in self.rg.map: self.annotate_grid(g) @@ -1167,6 +1166,14 @@ class router(router_tech): width=pin.width(), height=pin.height()) + def get_perimeter_pin(self): + """ Return the shape of the last routed path that was on the perimeter """ + for v in self.paths[-1]: + if self.rg.is_target(v): + return self.convert_track_to_pin(v) + + return None + def get_pin(self, pin_name): """ Return the lowest, leftest pin group """ keep_pin = None diff --git a/compiler/router/signal_exit_router.py b/compiler/router/signal_exit_router.py new file mode 100644 index 00000000..f769f342 --- /dev/null +++ b/compiler/router/signal_exit_router.py @@ -0,0 +1,89 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import debug +from globals import print_time +from router import router +from datetime import datetime +from supply_grid import supply_grid + +class signal_exit_router(router): + """ + A router that routes signals to perimeter and makes pins. + """ + + def __init__(self, layers, design, gds_filename=None): + """ + This will route on layers in design. It will get the blockages from + either the gds file name or the design itself (by saving to a gds file). + """ + router.__init__(self, layers, design, gds_filename, 1) + + def create_routing_grid(self): + """ + Create a sprase routing grid with A* expansion functions. + """ + size = self.ur - self.ll + debug.info(1,"Size: {0} x {1}".format(size.x, size.y)) + self.rg = supply_grid(self.ll, self.ur, self.track_width) + + def exit_route(self, pin_list): + """ + Takes a list of tuples (name, side) and routes them. After routing, + it removes the old pin and places a new one on the perimeter. + """ + pin_names = [x[0] for x in pin_list] + + # Clear the pins if we have previously routed + if (hasattr(self,'rg')): + self.clear_pins() + else: + self.create_routing_grid() + + # Get the pin shapes + start_time = datetime.now() + self.find_pins_and_blockages(pin_names) + print_time("Finding pins and blockages",datetime.now(), start_time, 3) + + # Route the supply pins to the supply rails + # Route vdd first since we want it to be shorter + start_time = datetime.now() + for pin_name, side in pin_list: + self.route_signal(pin_name, side) + + print_time("Maze routing pins",datetime.now(), start_time, 3) + + return True + + def route_signal(self, pin_name, side): + + for detour_scale in [5 * pow(2, x) for x in range(5)]: + debug.info(1, "Exit routing {0} with scale {1}".format(pin_name, detour_scale)) + + # Clear everything in the routing grid. + self.rg.reinit() + + # This is inefficient since it is non-incremental, but it was + # easier to debug. + self.prepare_blockages(pin_name) + + # Add the single component of the pin as the source + # which unmarks it as a blockage too + self.add_source(pin_name) + + # Marks the grid cells all along the perimeter as a target + self.add_perimeter_target(side) + + # Actually run the A* router + if self.run_router(detour_scale=detour_scale): + new_pin = self.get_perimeter_pin() + self.cell.replace_layout_pin(pin_name, new_pin) + return + + self.write_debug_gds("debug_route.gds", True) + + diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 5d536b0a..2278fd39 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -12,7 +12,7 @@ from datetime import datetime import grid_utils from scipy.sparse import csr_matrix from scipy.sparse.csgraph import minimum_spanning_tree - +from signal_grid import signal_grid class supply_tree_router(router): """ @@ -36,9 +36,7 @@ class supply_tree_router(router): """ size = self.ur - self.ll debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) - - import signal_grid - self.rg = signal_grid.signal_grid(self.ll, self.ur, self.route_track_width) + self.rg = signal_grid(self.ll, self.ur, self.route_track_width) def route(self, vdd_name="vdd", gnd_name="gnd"): """ diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a71d5e62..e1308fc1 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -8,7 +8,6 @@ from vector import vector from sram_base import sram_base from contact import m2_via -from globals import OPTS import channel_route @@ -106,7 +105,7 @@ class sram_1bank(sram_base): # We need to temporarily add some pins for the x offsets # but we'll remove them so that they have the right y # offsets after the DFF placement. - self.add_layout_pins() + self.add_layout_pins(exit_route=False) self.route_dffs(add_routes=False) self.remove_layout_pins() @@ -245,7 +244,7 @@ class sram_1bank(sram_base): self.data_pos[port] = vector(x_offset, y_offset) self.spare_wen_pos[port] = vector(x_offset, y_offset) - def add_layout_pins(self): + def add_layout_pins(self, exit_route=True): """ Add the top-level pins for a single bank SRAM with control. """ @@ -312,14 +311,16 @@ class sram_1bank(sram_base): "spare_wen{0}[{1}]".format(port, bit)) all_pins.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top)) - if OPTS.perimeter_pins: + if exit_route: from signal_exit_router import signal_exit_router as router rtr=router(self.m3_stack, self) - rtr.route(all_pins) + rtr.exit_route(all_pins) def route_layout(self): """ Route a single bank SRAM """ + self.route_supplies() + self.add_layout_pins() self.route_clk() @@ -329,6 +330,7 @@ class sram_1bank(sram_base): self.route_row_addr_dff() self.route_dffs() + def route_dffs(self, add_routes=True): @@ -364,16 +366,6 @@ class sram_1bank(sram_base): bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] route_map.extend(list(zip(bank_pins, dff_pins))) - if port in self.readwrite_ports and OPTS.perimeter_pins: - # outputs from sense amp - # These are the output pins which had their pin placed on the perimeter, so route from the - # sense amp which should not align with write driver input - sram_names = ["dout{0}[{1}]".format(port, x) for x in range(self.word_size + self.num_spare_cols)] - sram_pins = [self.get_pin(x) for x in sram_names] - bank_names = ["dout{0}_{1}".format(port, x) for x in range(self.word_size + self.num_spare_cols)] - bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] - route_map.extend(list(zip(bank_pins, sram_pins))) - # spare wen dff if self.num_spare_cols > 0 and port in self.write_ports: dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 490d01df..a02acc28 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -190,7 +190,7 @@ class sram_base(design, verilog, lef): start_time = datetime.datetime.now() self.route_layout() - self.route_supplies() + if not OPTS.is_unit_test: print_time("Routing", datetime.datetime.now(), start_time)