From 348001b1c8efad1ac5c1cddda21c9dd27943e66a Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 21 Dec 2020 13:51:50 -0800 Subject: [PATCH] Supply tree uses signal grid. PEP8 cleanup. --- .../riscv-scn4m_subm-1kbyte-1rw1r.py | 2 +- .../riscv-scn4m_subm-8kbyte-1rw1r.py | 4 +- compiler/router/router.py | 13 ++++- compiler/router/router_tech.py | 26 ++++----- compiler/router/signal_grid.py | 54 +++++++++---------- compiler/router/signal_router.py | 21 +++----- compiler/router/supply_grid.py | 13 ++--- compiler/router/supply_grid_router.py | 16 ++---- compiler/router/supply_tree_router.py | 28 ++++------ compiler/sram/sram.py | 22 ++++---- compiler/sram/sram_1bank.py | 33 ++++++++---- compiler/tests/configs/config.py | 4 +- compiler/verify/__init__.py | 1 - compiler/verify/magic.py | 1 - compiler/verify/none.py | 4 +- 15 files changed, 114 insertions(+), 128 deletions(-) diff --git a/compiler/example_configs/riscv-scn4m_subm-1kbyte-1rw1r.py b/compiler/example_configs/riscv-scn4m_subm-1kbyte-1rw1r.py index 1a3e3674..3a66d307 100644 --- a/compiler/example_configs/riscv-scn4m_subm-1kbyte-1rw1r.py +++ b/compiler/example_configs/riscv-scn4m_subm-1kbyte-1rw1r.py @@ -11,7 +11,7 @@ nominal_corner_only = True route_supplies = True check_lvsdrc = True -perimeter_pins = True +perimeter_pins = False #netlist_only = True #analytical_delay = False output_name = "sram_{0}rw{1}r{2}w_{3}_{4}_{5}".format(num_rw_ports, diff --git a/compiler/example_configs/riscv-scn4m_subm-8kbyte-1rw1r.py b/compiler/example_configs/riscv-scn4m_subm-8kbyte-1rw1r.py index b15a9f01..27620478 100644 --- a/compiler/example_configs/riscv-scn4m_subm-8kbyte-1rw1r.py +++ b/compiler/example_configs/riscv-scn4m_subm-8kbyte-1rw1r.py @@ -11,8 +11,8 @@ num_w_ports = 0 tech_name = "scn4m_subm" nominal_corner_only = True -route_supplies = False -check_lvsdrc = False +route_supplies = "tree" +check_lvsdrc = True perimeter_pins = False #netlist_only = True #analytical_delay = False diff --git a/compiler/router/router.py b/compiler/router/router.py index e1fa72ab..8069ad4a 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -27,13 +27,14 @@ class router(router_tech): route on a given layer. This is limited to two layer routes. It populates blockages on a grid class. """ - def __init__(self, layers, design, gds_filename=None, rail_track_width=1): + def __init__(self, layers, design, gds_filename=None, route_track_width=1): """ This will instantiate a copy of the gds file or the module at (0,0) and route on top of this. The blockages from the gds/module will be considered. """ - router_tech.__init__(self, layers, rail_track_width) + + router_tech.__init__(self, layers, route_track_width) self.cell = design @@ -1165,6 +1166,14 @@ class router(router_tech): return keep_pin + def check_all_routed(self, pin_name): + """ + Check that all pin groups are routed. + """ + for pg in self.pin_groups[pin_name]: + if not pg.is_routed(): + return False + # FIXME: This should be replaced with vector.snap_to_grid at some point def snap_to_grid(offset): diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 2cf31020..b34e4233 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -16,13 +16,13 @@ class router_tech: """ This is a class to hold the router tech constants. """ - def __init__(self, layers, rail_track_width): + def __init__(self, layers, route_track_width): """ Allows us to change the layers that we are routing on. This uses the preferreed directions. """ self.layers = layers - self.rail_track_width = rail_track_width + self.route_track_width = route_track_width if len(self.layers) == 1: self.horiz_layer_name = self.vert_layer_name = self.layers[0] @@ -55,7 +55,7 @@ class router_tech: "preferred_directions '{}' and '{}'.") via_connect = contact(self.layers, (1, 1)) - max_via_size = max(via_connect.width,via_connect.height) + max_via_size = max(via_connect.width, via_connect.height) self.horiz_lpp = layer[self.horiz_layer_name] self.vert_lpp = layer[self.vert_layer_name] @@ -71,16 +71,16 @@ class router_tech: self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing # We'll keep horizontal and vertical tracks the same for simplicity. - self.track_width = max(self.horiz_track_width,self.vert_track_width) - debug.info(1,"Track width: {:.3f}".format(self.track_width)) - self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing) - debug.info(1,"Track space: {:.3f}".format(self.track_space)) + self.track_width = max(self.horiz_track_width, self.vert_track_width) + debug.info(1, "Track width: {:.3f}".format(self.track_width)) + self.track_space = max(self.horiz_layer_spacing, self.vert_layer_spacing) + debug.info(1, "Track space: {:.3f}".format(self.track_space)) self.track_wire = self.track_width - self.track_space - debug.info(1,"Track wire width: {:.3f}".format(self.track_wire)) + debug.info(1, "Track wire width: {:.3f}".format(self.track_wire)) self.track_widths = vector([self.track_width] * 2) self.track_factor = vector([1/self.track_width] * 2) - debug.info(2,"Track factor: {}".format(self.track_factor)) + debug.info(2, "Track factor: {}".format(self.track_factor)) # When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side) self.layer_widths = [self.track_wire, 1, self.track_wire] @@ -107,7 +107,7 @@ class router_tech: elif zindex==0: return self.horiz_layer_name else: - debug.error("Invalid zindex {}".format(zindex),-1) + debug.error("Invalid zindex {}".format(zindex), -1) def get_supply_layer_width_space(self, zindex): """ @@ -123,9 +123,9 @@ class router_tech: min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) - min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf) - min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf) + min_width = drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf) + min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf) - return (min_width,min_spacing) + return (min_width, min_spacing) diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 4d9efa3e..28f092a5 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from itertools import tee import debug from heapq import heappush,heappop from copy import deepcopy @@ -14,6 +13,7 @@ from grid import grid from grid_path import grid_path from vector3d import vector3d + class signal_grid(grid): """ Expand the two layer grid to include A* search functions for a source and target. @@ -34,11 +34,11 @@ class signal_grid(grid): p.reset() # clear source and target pins - self.source=[] - self.target=[] + self.source = set() + self.target = set() # Clear the queue - while len(self.q)>0: + while len(self.q) > 0: heappop(self.q) self.counter = 0 @@ -49,19 +49,18 @@ class signal_grid(grid): We will use an A* search, so this cost must be pessimistic. Cost so far will be the length of the path. """ - #debug.info(3,"Initializing queue.") + #debug.info(3, "Initializing queue.") # 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.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)]))) + debug.info(3, "Init: cost=" + str(cost) + " " + str([s])) + heappush(self.q, (cost, self.counter, grid_path([vector3d(s)]))) self.counter+=1 - - def route(self,detour_scale): + def route(self, detour_scale): """ This does the A* maze routing with preferred direction routing. This only works for 1 track wide routes! @@ -70,7 +69,7 @@ class signal_grid(grid): # We set a cost bound of the HPWL for run-time. This can be # over-ridden if the route fails due to pruning a feasible solution. any_source_element = next(iter(self.source)) - cost_bound = detour_scale*self.cost_to_target(any_source_element)*grid.PREFERRED_COST + cost_bound = detour_scale * self.cost_to_target(any_source_element) * grid.PREFERRED_COST # Check if something in the queue is already a source and a target! for s in self.source: @@ -83,20 +82,17 @@ class signal_grid(grid): # Put the source items into the queue self.init_queue() - cheapest_path = None - cheapest_cost = None - # Keep expanding and adding to the priority queue until we are done while len(self.q)>0: # should we keep the path in the queue as well or just the final node? - (cost,count,curpath) = heappop(self.q) - debug.info(3,"Queue size: size=" + str(len(self.q)) + " " + str(cost)) - debug.info(4,"Expanding: cost=" + str(cost) + " " + str(curpath)) + (cost, count, curpath) = heappop(self.q) + debug.info(3, "Queue size: size=" + str(len(self.q)) + " " + str(cost)) + debug.info(4, "Expanding: cost=" + str(cost) + " " + str(curpath)) # expand the last element neighbors = self.expand_dirs(curpath) - debug.info(4,"Neighbors: " + str(neighbors)) + debug.info(4, "Neighbors: " + str(neighbors)) for n in neighbors: # make a new copy of the path to not update the old ones @@ -105,7 +101,7 @@ class signal_grid(grid): newpath.append(n) # check if we hit the target and are done if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide - return (newpath,newpath.cost()) + return (newpath, newpath.cost()) else: # current path cost + predicted cost current_cost = newpath.cost() @@ -116,18 +112,18 @@ class signal_grid(grid): if (self.map[n[0]].min_cost==-1 or predicted_cost self.ur.x or wave[-1].y > self.ur.y: return None @@ -57,7 +54,6 @@ class supply_grid(signal_grid): return wave - def is_wave_blocked(self, wave): """ Checks if any of the locations are blocked @@ -68,7 +64,6 @@ class supply_grid(signal_grid): else: return False - def probe(self, wave, direct): """ Expand the wave until there is a blockage and return diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 21369003..0bb6fb46 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -28,9 +28,9 @@ class supply_grid_router(router): start_time = datetime.now() # Power rail width in minimum wire widths - self.rail_track_width = 3 + self.route_track_width = 3 - router.__init__(self, layers, design, gds_filename, self.rail_track_width) + router.__init__(self, layers, design, gds_filename, self.route_track_width) # The list of supply rails (grid sets) that may be routed self.supply_rails = {} @@ -47,7 +47,7 @@ class supply_grid_router(router): debug.info(1, "Size: {0} x {1}".format(size.x, size.y)) import supply_grid - self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) + self.rg = supply_grid.supply_grid(self.ll, self.ur, self.route_track_width) def route(self, vdd_name="vdd", gnd_name="gnd"): """ @@ -104,14 +104,6 @@ class supply_grid_router(router): return True - def check_all_routed(self, pin_name): - """ - Check that all pin groups are routed. - """ - for pg in self.pin_groups[pin_name]: - if not pg.is_routed(): - return False - def route_simple_overlaps(self, pin_name): """ This checks for simple cases where a pin component already overlaps a supply rail. @@ -317,7 +309,7 @@ class supply_grid_router(router): data structure. Return whether it was added or not. """ # We must have at least 2 tracks to drop plus 2 tracks for a via - if len(wave_path) >= 4 * self.rail_track_width: + if len(wave_path) >= 4 * self.route_track_width: grid_set = wave_path.get_grids() self.supply_rails[name].append(grid_set) return True diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 4af2ed6a..06def0d7 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -1,9 +1,9 @@ # 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. +# 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 @@ -26,9 +26,9 @@ class supply_tree_router(router): either the gds file name or the design itself (by saving to a gds file). """ # Power rail width in minimum wire widths - self.rail_track_width = 3 + self.route_track_width = 3 - router.__init__(self, layers, design, gds_filename, self.rail_track_width) + router.__init__(self, layers, design, gds_filename, self.route_track_width) def create_routing_grid(self): """ @@ -37,8 +37,8 @@ class supply_tree_router(router): size = self.ur - self.ll debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) - import supply_grid - self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) + import signal_grid + self.rg = signal_grid.signal_grid(self.ll, self.ur, self.route_track_width) def route(self, vdd_name="vdd", gnd_name="gnd"): """ @@ -85,15 +85,6 @@ class supply_tree_router(router): return True - - def check_all_routed(self, pin_name): - """ - Check that all pin groups are routed. - """ - for pg in self.pin_groups[pin_name]: - if not pg.is_routed(): - return False - def prepare_blockages(self, pin_name): """ Reset and add all of the blockages in the design. @@ -125,7 +116,6 @@ class supply_tree_router(router): blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} self.set_blockages(blockage_grids,False) - def route_pins(self, pin_name): """ This will route each of the remaining pin components to the other pins. @@ -137,6 +127,7 @@ class supply_tree_router(router): remaining_components)) # Create full graph + debug.info(2,"Creating adjacency matrix") pin_size = len(self.pin_groups[pin_name]) adj_matrix = [[0] * pin_size for i in range(pin_size)] @@ -148,6 +139,7 @@ class supply_tree_router(router): adj_matrix[index1][index2] = dist # Find MST + debug.info(2,"Finding MinimumSpanning Tree") X = csr_matrix(adj_matrix) Tcsr = minimum_spanning_tree(X) mst = Tcsr.toarray().astype(int) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 4fdb1f9b..f80101e9 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -87,6 +87,17 @@ class sram(): def save(self): """ Save all the output files while reporting time to do it as well. """ + # Save the spice file + start_time = datetime.datetime.now() + spname = OPTS.output_path + self.s.name + ".sp" + debug.print_raw("SP: Writing to {0}".format(spname)) + self.sp_write(spname) + functional(self.s, + os.path.basename(spname), + cycles=200, + output_path=OPTS.output_path) + print_time("Spice writing", datetime.datetime.now(), start_time) + if not OPTS.netlist_only: # Write the layout start_time = datetime.datetime.now() @@ -107,17 +118,6 @@ class sram(): self.lef_write(lefname) print_time("LEF", datetime.datetime.now(), start_time) - # Save the spice file - start_time = datetime.datetime.now() - spname = OPTS.output_path + self.s.name + ".sp" - debug.print_raw("SP: Writing to {0}".format(spname)) - self.sp_write(spname) - functional(self.s, - os.path.basename(spname), - cycles=200, - output_path=OPTS.output_path) - print_time("Spice writing", datetime.datetime.now(), start_time) - # Save the LVS file start_time = datetime.datetime.now() lvsname = OPTS.output_path + self.s.name + ".lvs.sp" diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a7bc9c9e..e2f34780 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -5,7 +5,6 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import debug from vector import vector from sram_base import sram_base from contact import m2_via @@ -102,12 +101,17 @@ class sram_1bank(sram_base): # Place with an initial wide channel (from above) self.place_dffs() + # Route the channel and set to the new data bus size + # 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.route_dffs(add_routes=False) + self.remove_layout_pins() + # Re-place with the new channel size self.place_dffs() - # Now route the channel - self.route_dffs() def place_row_addr_dffs(self): """ @@ -298,15 +302,20 @@ class sram_1bank(sram_base): "din{0}[{1}]".format(port, bit)) # Data output pins go to BOTTOM/TOP - if port in self.readwrite_ports and OPTS.perimeter_pins: + if port in self.readwrite_ports: for bit in range(self.word_size + self.num_spare_cols): - # This should be routed next to the din pin - p = din_ports[bit] - self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit), - layer=p.layer, - offset=p.center() + vector(self.m3_pitch, 0), - width=p.width(), - height=p.height()) + if OPTS.perimeter_pins: + # This should be routed next to the din pin + p = din_ports[bit] + self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(port, bit), + layer=p.layer, + offset=p.center() + vector(self.m3_pitch, 0), + width=p.width(), + height=p.height()) + else: + self.copy_layout_pin(self.bank_inst, + "dout{0}_{1}".format(port, bit), + "dout{0}[{1}]".format(port, bit)) elif port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): if OPTS.perimeter_pins: @@ -384,6 +393,8 @@ class sram_1bank(sram_base): self.route_row_addr_dff() + self.route_dffs() + def route_dffs(self, add_routes=True): for port in self.all_ports: diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index 0682ef6e..3547a527 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -11,7 +11,9 @@ num_words = 16 tech_name = OPTS.tech_name +# perimeter_pins = True + nominal_corner_only = True -route_supplies = "tree" +route_supplies = "grid" check_lvsdrc = True diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index 136d6b45..7584c09c 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -23,7 +23,6 @@ from tech import lvs_name from tech import pex_name debug.info(1, "Initializing verify...") - if not OPTS.check_lvsdrc: debug.info(1, "LVS/DRC/PEX disabled.") OPTS.drc_exe = None diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index b3d2d99b..ad69a224 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -68,7 +68,6 @@ num_pex_runs = 0 def write_drc_script(cell_name, gds_name, extract, final_verification, output_path, sp_name=None): """ Write a magic script to perform DRC and optionally extraction. """ - global OPTS # Copy .magicrc file into the output directory diff --git a/compiler/verify/none.py b/compiler/verify/none.py index bd2fb722..4546d2eb 100644 --- a/compiler/verify/none.py +++ b/compiler/verify/none.py @@ -17,8 +17,8 @@ lvs_warned = False pex_warned = False -def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None): - pass +def write_drc_script(cell_name, gds_name, extract, final_verification=False, output_path=None, sp_name=None): + debug.error("Cannot write DRC script for unknown tool", -1) def run_drc(cell_name, gds_name, sp_name, extract=False, final_verification=False, output_path=None):