diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index a4c7a74d..f4f4f2e4 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -1197,6 +1197,18 @@ class layout(): elif add_vias: self.add_power_pin(name, pin.center(), start_layer=pin.layer) + def add_io_pin(self, instance, pin_name, new_name=""): + """ + Add a signle input or output pin up to metal 3. + """ + pin = instance.get_pin(pin_name) + + if new_name == "": + new_name = pin_name + + # Just use the power pin function for now to save code + self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer) + def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"): """ Add a single power pin from the lowest power_grid layer down to M1 (or li) at diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 54ffb612..b8e78290 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -5,12 +5,11 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -import numpy as np -import string import debug from vector3d import vector3d from grid_cell import grid_cell + class grid: """ A two layer routing map. Each cell can be blocked in the vertical @@ -23,7 +22,6 @@ class grid: NONPREFERRED_COST = 4 PREFERRED_COST = 1 - def __init__(self, ll, ur, track_width): """ Initialize the map and define the costs. """ @@ -33,12 +31,12 @@ class grid: self.track_width = track_width self.track_widths = [self.track_width, self.track_width, 1.0] - self.track_factor = [1/self.track_width, 1/self.track_width, 1.0] + self.track_factor = [1 / self.track_width, 1 / self.track_width, 1.0] # The bounds are in grids for this # This is really lower left bottom layer and upper right top layer in 3D. - self.ll = vector3d(ll.x,ll.y,0).scale(self.track_factor).round() - self.ur = vector3d(ur.x,ur.y,1).scale(self.track_factor).round() + self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round() + self.ur = vector3d(ur.x, ur.y, 1).scale(self.track_factor).round() # let's leave the map sparse, cells are created on demand to reduce memory self.map={} @@ -46,18 +44,18 @@ class grid: def add_all_grids(self): for x in range(self.ll.x, self.ur.x, 1): for y in range(self.ll.y, self.ur.y, 1): - self.add_map(vector3d(x,y,0)) - self.add_map(vector3d(x,y,1)) + self.add_map(vector3d(x, y, 0)) + self.add_map(vector3d(x, y, 1)) - def set_blocked(self,n,value=True): + def set_blocked(self, n, value=True): if not isinstance(n, vector3d): for item in n: - self.set_blocked(item,value) + self.set_blocked(item, value) else: self.add_map(n) self.map[n].blocked=value - def is_blocked(self,n): + def is_blocked(self, n): if not isinstance(n, vector3d): for item in n: if self.is_blocked(item): @@ -68,11 +66,10 @@ class grid: self.add_map(n) return self.map[n].blocked - - def set_path(self,n,value=True): - if isinstance(n, (list,tuple,set,frozenset)): + def set_path(self, n, value=True): + if isinstance(n, (list, tuple, set, frozenset)): for item in n: - self.set_path(item,value) + self.set_path(item, value) else: self.add_map(n) self.map[n].path=value @@ -81,47 +78,89 @@ class grid: for k in self.map: self.map[k].blocked=False - def set_source(self,n,value=True): + def set_source(self, n, value=True): if not isinstance(n, vector3d): for item in n: - self.set_source(item,value) + self.set_source(item, value) else: self.add_map(n) self.map[n].source=value self.source.add(n) - def set_target(self,n,value=True): + def set_target(self, n, value=True): if not isinstance(n, vector3d): for item in n: - self.set_target(item,value) + self.set_target(item, value) else: self.add_map(n) self.map[n].target=value self.target.add(n) - - def add_source(self,track_list,value=True): - debug.info(3,"Adding source list={0}".format(str(track_list))) + def add_source(self, track_list, value=True): + 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) + debug.info(4, "Adding source ={0}".format(str(n))) + self.set_source(n, value) + self.set_blocked(n, False) - - def add_target(self,track_list,value=True): - debug.info(3,"Adding target list={0}".format(str(track_list))) + def add_target(self, track_list, value=True): + 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) + debug.info(4, "Adding target ={0}".format(str(n))) + self.set_target(n, value) + self.set_blocked(n, False) - def is_target(self,point): + def add_perimeter_target(self, side="all", value=True): + debug.info(3, "Adding perimeter target") + + # 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) + + 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) + + 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) + + 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) + + def is_target(self, point): """ Point is in the target set, so we are done. """ return point in self.target - def add_map(self,n): + def add_map(self, n): """ Add a point to the map if it doesn't exist. """ @@ -132,8 +171,7 @@ class grid: if n not in self.map: self.map[n]=grid_cell() - - def block_path(self,path): + def block_path(self, path): """ Mark the path in the routing grid as blocked. Also unsets the path flag. diff --git a/compiler/router/router.py b/compiler/router/router.py index 8069ad4a..09f691f7 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -286,7 +286,7 @@ class router(router_tech): If so, reduce the pin group grid to not include the adjacent grid. Try to do this intelligently to keep th pins enclosed. """ - debug.info(1, + debug.info(2, "Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) removed_grids = 0 @@ -302,7 +302,7 @@ class router(router_tech): adj_grids)) self.remove_adjacent_grid(pg1, pg2, adj_grids) - debug.info(1, "Removed {} adjacent grids.".format(removed_grids)) + debug.info(2, "Removed {} adjacent grids.".format(removed_grids)) def remove_adjacent_grid(self, pg1, pg2, adj_grids): """ @@ -348,6 +348,10 @@ class router(router_tech): smaller)) smaller.grids.remove(adj) + def set_supply_rail_blocked(self, value): + # This is just a virtual function + pass + def prepare_blockages(self, pin_name): """ Reset and add all of the blockages in the design. @@ -363,7 +367,11 @@ class router(router_tech): # Block all of the supply rails # (some will be unblocked if they're a target) - self.set_supply_rail_blocked(True) + try: + self.set_supply_rail_blocked(True) + except AttributeError: + # If function doesn't exist, it isn't a supply router + pass # Block all of the pin components # (some will be unblocked if they're a source/target) @@ -798,7 +806,7 @@ class router(router_tech): """ Convert the pin groups into pin tracks and blockage tracks. """ - debug.info(1, "Converting pins for {}.".format(pin_name)) + debug.info(2, "Converting pins for {}.".format(pin_name)) for pg in self.pin_groups[pin_name]: pg.convert_pin() @@ -809,7 +817,7 @@ class router(router_tech): that are blocked by other shapes. """ for pin_name in self.pin_groups: - debug.info(1, "Enclosing pins for {}".format(pin_name)) + debug.info(2, "Enclosing pins for {}".format(pin_name)) for pg in self.pin_groups[pin_name]: pg.enclose_pin() pg.add_enclosure(self.cell) @@ -830,6 +838,12 @@ class router(router_tech): for i in range(self.num_pin_components(pin_name)): self.add_pin_component_target(pin_name, i) + def add_perimeter_target(self, side="all"): + """ + This will mark all the cells on the perimeter of the original layout as a target. + """ + self.rg.add_perimeter_target(side=side) + def num_pin_components(self, pin_name): """ This returns how many disconnected pin components there are. @@ -902,7 +916,7 @@ class router(router_tech): # self.write_debug_gds() # First, simplify the path for - # debug.info(1, str(self.path)) + # debug.info(3, str(self.path)) contracted_path = self.contract_path(path) debug.info(3, "Contracted path: " + str(contracted_path)) diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py index 28f092a5..ced18147 100644 --- a/compiler/router/signal_grid.py +++ b/compiler/router/signal_grid.py @@ -49,8 +49,6 @@ 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.") - # 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 diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 0bb6fb46..db892986 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -74,6 +74,7 @@ class supply_grid_router(router): start_time = datetime.now() # Block everything self.prepare_blockages(self.gnd_name) + # Determine the rail locations self.route_supply_rails(self.gnd_name, 0) diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 06def0d7..5d536b0a 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -85,37 +85,6 @@ class supply_tree_router(router): return True - def prepare_blockages(self, pin_name): - """ - Reset and add all of the blockages in the design. - Names is a list of pins to add as a blockage. - """ - debug.info(3,"Preparing blockages.") - - # 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) - self.set_blockages(self.blocked_grids,True) - - # Block all of the pin components (some will be unblocked if they're a source/target) - # Also block the previous routes - for name in self.pin_groups: - blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} - self.set_blockages(blockage_grids,True) - blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} - self.set_blockages(blockage_grids,True) - - # FIXME: These duplicate a bit of work - # These are the paths that have already been routed. - self.set_blockages(self.path_blockages) - - # 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) - def route_pins(self, pin_name): """ This will route each of the remaining pin components to the other pins. @@ -187,3 +156,14 @@ class supply_tree_router(router): + def add_io_pin(self, instance, pin_name, new_name=""): + """ + Add a signle input or output pin up to metal 3. + """ + pin = instance.get_pins(pin_name) + + if new_name == "": + new_name = pin_name + + # Just use the power pin function for now to save code + self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index e2f34780..a71d5e62 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -249,138 +249,73 @@ class sram_1bank(sram_base): """ Add the top-level pins for a single bank SRAM with control. """ - highest_coord = self.find_highest_coords() - lowest_coord = self.find_lowest_coords() - bbox = [lowest_coord, highest_coord] - + # List of pin to new pin name + all_pins = [] 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 == "clk": - continue if signal.startswith("rbl"): continue - if OPTS.perimeter_pins: - self.add_perimeter_pin(name=signal + "{}".format(port), - pin=self.control_logic_insts[port].get_pin(signal), - side=left_or_right, - bbox=bbox) + self.add_io_pin(self.control_logic_insts[port], + signal, + signal + "{}".format(port)) + if signal=="clk": + all_pins.append(("{0}{1}".format(signal, port), bottom_or_top)) else: - self.copy_layout_pin(self.control_logic_insts[port], - signal, - signal + "{}".format(port)) - - if OPTS.perimeter_pins: - self.add_perimeter_pin(name="clk{}".format(port), - pin=self.control_logic_insts[port].get_pin("clk"), - side=bottom_or_top, - bbox=bbox) - else: - self.copy_layout_pin(self.control_logic_insts[port], - "clk", - "clk{}".format(port)) - - # Data input pins go to BOTTOM/TOP - din_ports = [] + all_pins.append(("{0}{1}".format(signal, port), left_or_right)) + if port in self.write_ports: for bit in range(self.word_size + self.num_spare_cols): - if OPTS.perimeter_pins: - p = self.add_perimeter_pin(name="din{0}[{1}]".format(port, bit), - pin=self.data_dff_insts[port].get_pin("din_{0}".format(bit)), - side=bottom_or_top, - bbox=bbox) - din_ports.append(p) - else: - self.copy_layout_pin(self.data_dff_insts[port], - "din_{}".format(bit), - "din{0}[{1}]".format(port, bit)) + self.add_io_pin(self.data_dff_insts[port], + "din_{}".format(bit), + "din{0}[{1}]".format(port, bit)) + all_pins.append(("din{0}[{1}]".format(port, bit), bottom_or_top)) - # Data output pins go to BOTTOM/TOP - if port in self.readwrite_ports: + if port in self.readwrite_ports or port in self.read_ports: for bit in range(self.word_size + self.num_spare_cols): - 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: - # This should have a clear route to the perimeter if there are no din routes - self.add_perimeter_pin(name="dout{0}[{1}]".format(port, bit), - pin=self.bank_inst.get_pin("dout{0}_{1}".format(port, bit)), - side=bottom_or_top, - bbox=bbox) - else: - self.copy_layout_pin(self.bank_inst, - "dout{0}_{1}".format(port, bit), - "dout{0}[{1}]".format(port, bit)) + self.add_io_pin(self.bank_inst, + "dout{0}_{1}".format(port, bit), + "dout{0}[{1}]".format(port, bit)) + all_pins.append(("dout{0}[{1}]".format(port, bit), bottom_or_top)) - - - # Lower address bits go to BOTTOM/TOP for bit in range(self.col_addr_size): - if OPTS.perimeter_pins: - self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit), - pin=self.col_addr_dff_insts[port].get_pin("din_{}".format(bit)), - side=bottom_or_top, - bbox=bbox) - else: - self.copy_layout_pin(self.col_addr_dff_insts[port], - "din_{}".format(bit), - "addr{0}[{1}]".format(port, bit)) + self.add_io_pin(self.col_addr_dff_insts[port], + "din_{}".format(bit), + "addr{0}[{1}]".format(port, bit)) + + all_pins.append(("addr{0}[{1}]".format(port, bit), bottom_or_top)) - # Upper address bits go to LEFT/RIGHT for bit in range(self.row_addr_size): - if OPTS.perimeter_pins: - self.add_perimeter_pin(name="addr{0}[{1}]".format(port, bit + self.col_addr_size), - pin=self.row_addr_dff_insts[port].get_pin("din_{}".format(bit)), - side=left_or_right, - bbox=bbox) - else: - self.copy_layout_pin(self.row_addr_dff_insts[port], - "din_{}".format(bit), - "addr{0}[{1}]".format(port, bit + self.col_addr_size)) + self.add_io_pin(self.row_addr_dff_insts[port], + "din_{}".format(bit), + "addr{0}[{1}]".format(port, bit + self.col_addr_size)) + all_pins.append(("addr{0}[{1}]".format(port, bit + self.col_addr_size), left_or_right)) - # Write mask pins go to BOTTOM/TOP if port in self.write_ports: if self.write_size: for bit in range(self.num_wmasks): - if OPTS.perimeter_pins: - self.add_perimeter_pin(name="wmask{0}[{1}]".format(port, bit), - pin=self.wmask_dff_insts[port].get_pin("din_{}".format(bit)), - side=bottom_or_top, - bbox=bbox) - else: - self.copy_layout_pin(self.wmask_dff_insts[port], - "din_{}".format(bit), - "wmask{0}[{1}]".format(port, bit)) + self.add_io_pin(self.wmask_dff_insts[port], + "din_{}".format(bit), + "wmask{0}[{1}]".format(port, bit)) + all_pins.append(("wmask{0}[{1}]".format(port, bit), bottom_or_top)) - # Spare wen pins go to BOTTOM/TOP if port in self.write_ports: for bit in range(self.num_spare_cols): - if OPTS.perimeter_pins: - self.add_perimeter_pin(name="spare_wen{0}[{1}]".format(port, bit), - pin=self.spare_wen_dff_insts[port].get_pin("din_{}".format(bit)), - side=left_or_right, - bbox=bbox) - else: - self.copy_layout_pin(self.spare_wen_dff_insts[port], - "din_{}".format(bit), - "spare_wen{0}[{1}]".format(port, bit)) + self.add_io_pin(self.spare_wen_dff_insts[port], + "din_{}".format(bit), + "spare_wen{0}[{1}]".format(port, bit)) + all_pins.append(("spare_wen{0}[{1}]".format(port, bit), bottom_or_top)) + + if OPTS.perimeter_pins: + from signal_exit_router import signal_exit_router as router + rtr=router(self.m3_stack, self) + rtr.route(all_pins) def route_layout(self): """ Route a single bank SRAM """ diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index 3547a527..d6080041 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -11,9 +11,9 @@ num_words = 16 tech_name = OPTS.tech_name -# perimeter_pins = True +perimeter_pins = True nominal_corner_only = True -route_supplies = "grid" +route_supplies = "tree" check_lvsdrc = True