diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 177f50e0..debeceee 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -23,10 +23,14 @@ class geometry: A specific path, shape, or text geometry. Base class for shared items. """ - def __init__(self): + def __init__(self, lpp=None): """ By default, everything has no size. """ self.width = 0 self.height = 0 + if lpp: + self.lpp = lpp + self.layerNumber = lpp[0] + self.layerPurpose = lpp[1] def __str__(self): """ override print function output """ @@ -410,10 +414,8 @@ class path(geometry): def __init__(self, lpp, coordinates, path_width): """Initializes a path for the specified layer""" - super().__init__() + super().__init__(lpp) self.name = "path" - self.layerNumber = lpp[0] - self.layerPurpose = lpp[1] self.coordinates = map(lambda x: [x[0], x[1]], coordinates) self.coordinates = vector(self.coordinates).snap_to_grid() self.path_width = path_width @@ -448,11 +450,9 @@ class label(geometry): def __init__(self, text, lpp, offset, zoom=None): """Initializes a text label for specified layer""" - super().__init__() + super().__init__(lpp) self.name = "label" self.text = text - self.layerNumber = lpp[0] - self.layerPurpose = lpp[1] self.offset = vector(offset).snap_to_grid() if not zoom: @@ -495,10 +495,8 @@ class rectangle(geometry): def __init__(self, lpp, offset, width, height): """Initializes a rectangular shape for specified layer""" - super().__init__() + super().__init__(lpp) self.name = "rect" - self.layerNumber = lpp[0] - self.layerPurpose = lpp[1] self.offset = vector(offset).snap_to_grid() self.size = vector(width, height).snap_to_grid() self.width = round_to_grid(self.size.x) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 02e67228..1ec5a2f0 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -473,11 +473,25 @@ class layout(): """ self.pin_map = {} + def copy_layout_pin_shapes(self, text): + """ + Copy the shapes of the layout pins as objects. + """ + for s in self.pin_map[text]: + self.add_rect(layer=s.layer, + offset=s.ll(), + width=s.width(), + height=s.height()) + def replace_layout_pin(self, text, pin): """ Remove the old pin and replace with a new one """ + # Keep the shapes as they were used to connect to the router pins + self.copy_layout_pin_shapes(text) + # Remove the shapes as actual pins self.remove_layout_pin(text) + # Add the new pin self.add_layout_pin(text=text, layer=pin.layer, offset=pin.ll(), diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 58d26007..6b556649 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -108,7 +108,10 @@ class dff_buf(design.design): well_spacing = max(well_spacing, self.pwell_to_nwell) except AttributeError: pass - self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0)) + + well_spacing += self.well_extend_active + + self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing, 0)) # Add INV2 to the right self.inv2_inst.place(vector(self.inv1_inst.rx(), 0)) diff --git a/compiler/router/grid.py b/compiler/router/grid.py index b8e78290..ae6e04ee 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -78,81 +78,80 @@ class grid: for k in self.map: self.map[k].blocked=False - def set_source(self, n, value=True): + def clear_source(self): + for k in self.map: + self.map[k].source=False + self.source = set() + + def set_source(self, n): if not isinstance(n, vector3d): for item in n: - self.set_source(item, value) + self.set_source(item) else: self.add_map(n) - self.map[n].source=value + self.map[n].source=True + self.map[n].blocked=False self.source.add(n) - def set_target(self, n, value=True): + def clear_target(self): + for k in self.map: + self.map[k].target=False + self.target = set() + + def set_target(self, n): if not isinstance(n, vector3d): for item in n: - self.set_target(item, value) + self.set_target(item) else: self.add_map(n) - self.map[n].target=value + self.map[n].target=True + self.map[n].blocked=False self.target.add(n) - - def add_source(self, track_list, value=True): + + def add_source(self, track_list): debug.info(3, "Adding source list={0}".format(str(track_list))) for n in track_list: debug.info(4, "Adding source ={0}".format(str(n))) - self.set_source(n, value) - self.set_blocked(n, False) + self.set_source(n) + # self.set_blocked(n, False) - def add_target(self, track_list, value=True): + def add_target(self, track_list): debug.info(3, "Adding target list={0}".format(str(track_list))) for n in track_list: debug.info(4, "Adding target ={0}".format(str(n))) - self.set_target(n, value) - self.set_blocked(n, False) + self.set_target(n) + # self.set_blocked(n, False) - def add_perimeter_target(self, side="all", value=True): + def add_perimeter_target(self, side="all"): debug.info(3, "Adding perimeter target") + perimeter_list = [] # Add the left/right columns if side=="all" or side=="left": x = self.ll.x for y in range(self.ll.y, self.ur.y, 1): - n = vector3d(x, y, 0) - self.set_target(n, value) - self.set_blocked(n, False) - n = vector3d(x, y, 1) - self.set_target(n, value) - self.set_blocked(n, False) + perimeter_list.append(vector3d(x, y, 0)) + perimeter_list.append(vector3d(x, y, 1)) if side=="all" or side=="right": x = self.ur.x for y in range(self.ll.y, self.ur.y, 1): - n = vector3d(x, y, 0) - self.set_target(n, value) - self.set_blocked(n, False) - n = vector3d(x, y, 1) - self.set_target(n, value) - self.set_blocked(n, False) + perimeter_list.append(vector3d(x, y, 0)) + perimeter_list.append(vector3d(x, y, 1)) if side=="all" or side=="bottom": y = self.ll.y for x in range(self.ll.x, self.ur.x, 1): - n = vector3d(x, y, 0) - self.set_target(n, value) - self.set_blocked(n, False) - n = vector3d(x, y, 1) - self.set_target(n, value) - self.set_blocked(n, False) + perimeter_list.append(vector3d(x, y, 0)) + perimeter_list.append(vector3d(x, y, 1)) if side=="all" or side=="top": y = self.ur.y for x in range(self.ll.x, self.ur.x, 1): - n = vector3d(x, y, 0) - self.set_target(n, value) - self.set_blocked(n, False) - n = vector3d(x, y, 1) - self.set_target(n, value) - self.set_blocked(n, False) + perimeter_list.append(vector3d(x, y, 0)) + perimeter_list.append(vector3d(x, y, 1)) + + self.set_target(perimeter_list) def is_target(self, point): """ diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py index ba30fcef..0a89358d 100644 --- a/compiler/router/grid_cell.py +++ b/compiler/router/grid_cell.py @@ -34,7 +34,6 @@ class grid_cell: if self.min_cost > 0: return self.min_cost - def get_type(self): type_string = "" @@ -50,7 +49,5 @@ class grid_cell: if self.path: type_string += "P" - if type_string != "": - return type_string + return type_string - return None diff --git a/compiler/router/router.py b/compiler/router/router.py index 1825925b..d666fcb5 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -6,10 +6,11 @@ # All rights reserved. # +import itertools +import math import gdsMill from tech import drc, GDS from tech import layer as techlayer -import math import debug from router_tech import router_tech from pin_layout import pin_layout @@ -189,7 +190,6 @@ class router(router_tech): # self.combine_adjacent_pins(pin) # print_time("Combining adjacent pins",datetime.now(), start_time, 4) - # Separate any adjacent grids of differing net names # that overlap # Must be done before enclosing pins @@ -266,18 +266,10 @@ class router(router_tech): This will try to separate all grid pins by the supplied number of separation tracks (default is to prevent adjacency). """ - # Commented out to debug with SCMOS - # if separation==0: - # return - pin_names = self.pin_groups.keys() - for i, pin_name1 in enumerate(pin_names): - for j, pin_name2 in enumerate(pin_names): - if i == j: - continue - if i > j: - return - self.separate_adjacent_pin(pin_name1, pin_name2, separation) + + for (pin_name1, pin_name2) in itertools.combinations(pin_names, 2): + self.separate_adjacent_pin(pin_name1, pin_name2, separation) def separate_adjacent_pin(self, pin_name1, pin_name2, separation): """ @@ -290,6 +282,7 @@ class router(router_tech): "Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) removed_grids = 0 + for index1, pg1 in enumerate(self.pin_groups[pin_name1]): for index2, pg2 in enumerate(self.pin_groups[pin_name2]): adj_grids = pg1.adjacent_grids(pg2, separation) @@ -362,7 +355,7 @@ class router(router_tech): # Start fresh. Not the best for run-time, but simpler. self.clear_blockages() # This adds the initial blockges of the design - #print("BLOCKING:", self.blocked_grids) + # print("BLOCKING:", self.blocked_grids) self.set_blockages(self.blocked_grids, True) # Block all of the supply rails @@ -389,8 +382,9 @@ class router(router_tech): # Don't mark the other components as targets since we want to route # directly to a rail, but unblock all the source components so we can # route over them - blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} - self.set_blockages(blockage_grids, False) + # 1/6/21: This would cause things that looked like loops in the supply tree router + # blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} + # self.set_blockages(blockage_grids, False) def convert_shape_to_units(self, shape): """ @@ -1039,18 +1033,18 @@ class router(router_tech): (ll, ur) = self.convert_track_to_shape(coord) self.cell.add_rect(layer="text", offset=ll, - width=ur[0]-ll[0], - height=ur[1]-ll[1]) - (ll, ur) = self.convert_track_to_pin(coord).rect - self.cell.add_rect(layer="boundary", - offset=ll, - width=ur[0]-ll[0], - height=ur[1]-ll[1]) + width=ur[0] - ll[0], + height=ur[1] - ll[1]) + # (ll, ur) = self.convert_track_to_pin(coord).rect + # self.cell.add_rect(layer="boundary", + # offset=ll, + # width=ur[0] - ll[0], + # height=ur[1] - ll[1]) (ll, ur) = pin.rect self.cell.add_rect(layer="text", offset=ll, - width=ur[0]-ll[0], - height=ur[1]-ll[1]) + width=ur[0] - ll[0], + height=ur[1] - ll[1]) def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True): """ @@ -1058,9 +1052,9 @@ class router(router_tech): search information annotated on it. """ debug.info(0, "Writing annotated router gds file to {}".format(gds_name)) - self.del_router_info() self.add_router_info() self.cell.gds_write(gds_name) + self.del_router_info() if stop_program: import sys @@ -1071,17 +1065,17 @@ class router(router_tech): Display grid information in the GDS file for a single grid cell. """ shape = self.convert_track_to_shape(g) - partial_track = vector(0,self.track_width/6.0) + partial_track = vector(0, self.track_width / 6.0) self.cell.add_rect(layer="text", offset=shape[0], - width=shape[1].x-shape[0].x, - height=shape[1].y-shape[0].y) + width=shape[1].x - shape[0].x, + height=shape[1].y - shape[0].y) t = self.rg.map[g].get_type() # midpoint offset - off = vector((shape[1].x+shape[0].x)/2, - (shape[1].y+shape[0].y)/2) - if t != None: + off = vector((shape[1].x + shape[0].x) / 2, + (shape[1].y + shape[0].y) / 2) + if t: if g[2] == 1: # Upper layer is upper right label type_off = off + partial_track @@ -1107,16 +1101,15 @@ class router(router_tech): self.cell.add_label(text="{0},{1}".format(g[0], g[1]), layer="text", - offset=shape[0], - zoom=0.05) - + offset=shape[0]) + def del_router_info(self): """ Erase all of the comments on the current level. """ debug.info(0, "Erasing router info") - layer_num = techlayer["text"] - self.cell.objs = [x for x in self.cell.objs if x.layerNumber != layer_num] + lpp = techlayer["text"] + self.cell.objs = [x for x in self.cell.objs if x.lpp != lpp] def add_router_info(self): """ @@ -1132,7 +1125,6 @@ class router(router_tech): show_all_grids = True if show_all_grids: - # self.rg.add_all_grids() for g in self.rg.map: self.annotate_grid(g) @@ -1143,8 +1135,8 @@ class router(router_tech): (ll, ur) = blockage.inflate() self.cell.add_rect(layer="text", offset=ll, - width=ur.x-ll.x, - height=ur.y-ll.y) + width=ur.x - ll.x, + height=ur.y - ll.y) if show_blockage_grids: self.set_blockages(self.blocked_grids, True) for g in self.rg.map: diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 83232054..2c06d619 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -9,7 +9,7 @@ import debug from globals import print_time from router import router from datetime import datetime -from supply_grid import supply_grid +from signal_grid import signal_grid class signal_escape_router(router): @@ -30,31 +30,38 @@ class signal_escape_router(router): """ 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) + self.rg = signal_grid(self.ll, self.ur, self.track_width) - def escape_route(self, pin_list): + def perimeter_dist(self, pin_name): + """ + Return the shortest Manhattan distance to the bounding box perimeter. + """ + loc = self.cell.get_pin(pin_name).center() + x_dist = min(loc.x - self.ll.x, self.ur.x - loc.x) + y_dist = min(loc.y - self.ll.y, self.ur.y - loc.y) + + return min(x_dist, y_dist) + + def escape_route(self, pin_names): """ 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() + 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) + # Order the routes by closest to the perimeter first + # This prevents some pins near the perimeter from being blocked by other pins + ordered_pin_names = sorted(pin_names, key=lambda x: self.perimeter_dist(x)) + # 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) + for pin_name in ordered_pin_names: + self.route_signal(pin_name) print_time("Maze routing pins",datetime.now(), start_time, 3) @@ -62,10 +69,10 @@ class signal_escape_router(router): return True - def route_signal(self, pin_name, side): + def route_signal(self, pin_name, side="all"): 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)) + debug.info(1, "Escape routing {0} with scale {1}".format(pin_name, detour_scale)) # Clear everything in the routing grid. self.rg.reinit() diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index ced18147..a1820d88 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -23,9 +23,6 @@ class signal_grid(grid): """ Create a routing map of width x height cells and 2 in the z-axis. """ grid.__init__(self, ll, ur, track_factor) - # priority queue for the maze routing - self.q = [] - def reinit(self): """ Reinitialize everything for a new route. """ @@ -33,14 +30,8 @@ class signal_grid(grid): for p in self.map.values(): p.reset() - # clear source and target pins - self.source = set() - self.target = set() - - # Clear the queue - while len(self.q) > 0: - heappop(self.q) - self.counter = 0 + self.clear_source() + self.clear_target() def init_queue(self): """ @@ -51,12 +42,13 @@ class signal_grid(grid): """ # Counter is used to not require data comparison in Python 3.x # Items will be returned in order they are added during cost ties + self.q = [] self.counter = 0 for s in self.source: cost = self.cost_to_target(s) debug.info(3, "Init: cost=" + str(cost) + " " + str([s])) heappush(self.q, (cost, self.counter, grid_path([vector3d(s)]))) - self.counter+=1 + self.counter += 1 def route(self, detour_scale): """ @@ -72,11 +64,7 @@ class signal_grid(grid): # Check if something in the queue is already a source and a target! for s in self.source: if self.is_target(s): - return((grid_path([vector3d(s)]),0)) - - # Make sure the queue is empty if we run another route - while len(self.q)>0: - heappop(self.q) + return((grid_path([vector3d(s)]), 0)) # Put the source items into the queue self.init_queue() diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index b4149211..d2db1e29 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -144,15 +144,13 @@ class supply_tree_router(router): # Marks all pin components except index as target self.add_pin_component_target(pin_name, dest_idx) - + # Actually run the A* router if self.run_router(detour_scale=detour_scale): return self.write_debug_gds("debug_route.gds", True) - - def add_io_pin(self, instance, pin_name, new_name=""): """ Add a signle input or output pin up to metal 3. diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 2617d7f1..dc8917b6 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -254,43 +254,37 @@ class sram_1bank(sram_base): # List of pin to new pin name pins_to_route = [] for port in self.all_ports: - # Depending on the port, use the bottom/top or left/right sides - # Port 0 is left/bottom - # Port 1 is right/top - bottom_or_top = "bottom" if port==0 else "top" - left_or_right = "left" if port==0 else "right" - # Connect the control pins as inputs for signal in self.control_logic_inputs[port]: if signal.startswith("rbl"): continue if signal=="clk": - pins_to_route.append(("{0}{1}".format(signal, port), bottom_or_top)) + pins_to_route.append("{0}{1}".format(signal, port)) else: - pins_to_route.append(("{0}{1}".format(signal, port), left_or_right)) + pins_to_route.append("{0}{1}".format(signal, port)) if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): - pins_to_route.append(("din{0}[{1}]".format(port, bit), bottom_or_top)) + pins_to_route.append("din{0}[{1}]".format(port, bit)) if port in self.readwrite_ports or port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): - pins_to_route.append(("dout{0}[{1}]".format(port, bit), bottom_or_top)) + pins_to_route.append("dout{0}[{1}]".format(port, bit)) for bit in range(self.col_addr_size): - pins_to_route.append(("addr{0}[{1}]".format(port, bit), bottom_or_top)) + pins_to_route.append("addr{0}[{1}]".format(port, bit)) for bit in range(self.row_addr_size): - pins_to_route.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right)) + pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size)) if port in self.write_ports: if self.write_size: for bit in range(self.num_wmasks): - pins_to_route.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top)) + pins_to_route.append("wmask{0}[{1}]".format(port, bit)) if port in self.write_ports: for bit in range(self.num_spare_cols): - pins_to_route.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top)) + pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) rtr=router(self.m3_stack, self) rtr.escape_route(pins_to_route) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index ad69a224..21e7592e 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -88,9 +88,12 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa f.write("drc off\n") f.write("gds polygon subcell true\n") f.write("gds warning default\n") - f.write("gds flatten true\n") - f.write("gds readonly true\n") + # These two options are temporarily disabled until Tim fixes a bug in magic related + # to flattening channel routes and vias (hierarchy with no devices in it). Otherwise, + # they appear to be disconnected. + f.write("#gds flatten true\n") f.write("#gds ordering true\n") + f.write("gds readonly true\n") f.write("gds read {}\n".format(gds_name)) f.write('puts "Finished reading gds {}"\n'.format(gds_name)) f.write("load {}\n".format(cell_name))