diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index d71c7073..695a5432 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -495,6 +495,16 @@ class pin_group: self.grids = pg1.grids | pg2.grids # OR the set of grid locations self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids + def add_group(self, pg): + """ + Combine the pin group into this one. This will add to the first item in the pins + so this should be used before there are disconnected pins. + """ + debug.check(len(self.pins)==1,"Don't know which group to add pins to.") + self.pins[0].update(*pg.pins) # Join the two lists of pins + self.grids |= pg.grids # OR the set of grid locations + self.secondary_grids |= pg.secondary_grids + def add_enclosure(self, cell): """ Add the enclosure shape to the given cell. diff --git a/compiler/router/router.py b/compiler/router/router.py index 18d88df7..727d7753 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -9,9 +9,10 @@ from pin_layout import pin_layout from pin_group import pin_group from vector import vector from vector3d import vector3d -from globals import OPTS +from globals import OPTS,print_time from pprint import pformat import grid_utils +from datetime import datetime class router(router_tech): """ @@ -31,16 +32,18 @@ class router(router_tech): # If didn't specify a gds blockage file, write it out to read the gds # This isn't efficient, but easy for now + #start_time = datetime.now() if not gds_filename: gds_filename = OPTS.openram_temp+"temp.gds" self.cell.gds_write(gds_filename) - + # Load the gds file and read in all the shapes self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(gds_filename) self.top_name = self.layout.rootStructureName - + #print_time("GDS read",datetime.now(), start_time) + ### The pin data structures # A map of pin names to a set of pin_layout structures self.pins = {} @@ -127,8 +130,12 @@ class router(router_tech): Pin can either be a label or a location,layer pair: [[x,y],layer]. """ debug.info(1,"Finding pins for {}.".format(pin_name)) + #start_time = datetime.now() self.retrieve_pins(pin_name) + #print_time("Retrieved pins",datetime.now(), start_time) + #start_time = datetime.now() self.analyze_pins(pin_name) + #print_time("Analyzed pins",datetime.now(), start_time) def find_blockages(self): """ @@ -152,97 +159,98 @@ class router(router_tech): self.find_pins(pin) # This will get all shapes as blockages and convert to grid units - # This ignores shapes that were pins + # This ignores shapes that were pins + #start_time = datetime.now() self.find_blockages() + #print_time("Find blockags",datetime.now(), start_time) # Convert the blockages to grid units + #start_time = datetime.now() self.convert_blockages() + #print_time("Find blockags",datetime.now(), start_time) # This will convert the pins to grid units # It must be done after blockages to ensure no DRCs between expanded pins and blocked grids + #start_time = datetime.now() for pin in pin_list: self.convert_pins(pin) - + #print_time("Convert pins",datetime.now(), start_time) + + #start_time = datetime.now() for pin in pin_list: self.combine_adjacent_pins(pin) + #print_time("Combine pins",datetime.now(), start_time) #self.write_debug_gds("debug_combine_pins.gds",stop_program=True) # Separate any adjacent grids of differing net names to prevent wide metal DRC violations # Must be done before enclosing pins + #start_time = datetime.now() self.separate_adjacent_pins(self.supply_rail_space_width) + #print_time("Separate pins",datetime.now(), start_time) # For debug #self.separate_adjacent_pins(1) # Enclose the continguous grid units in a metal rectangle to fix some DRCs + #start_time = datetime.now() self.enclose_pins() + #print_time("Enclose pins",datetime.now(), start_time) #self.write_debug_gds("debug_enclose_pins.gds",stop_program=True) - def combine_adjacent_pins_pass(self, pin_name): + def combine_adjacent_pins(self, pin_name): """ Find pins that have adjacent routing tracks and merge them into a single pin_group. The pins themselves may not be touching, but enclose_pis in the next step will ensure they are touching. """ - - # Make a copy since we are going to add to (and then reduce) this list - pin_groups = self.pin_groups[pin_name].copy() - - # Start as None to signal the first iteration - remove_indices = set() - + debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) + # Find all adjacencies + adjacent_pins = {} for index1,pg1 in enumerate(self.pin_groups[pin_name]): - # Cannot combine more than once - if index1 in remove_indices: - continue for index2,pg2 in enumerate(self.pin_groups[pin_name]): - # Cannot combine with yourself - if index1==index2: + # Cannot combine with yourself, also don't repeat + if index1<=index2: continue - # Cannot combine more than once - if index2 in remove_indices: - continue - # Combine if at least 1 grid cell is adjacent if pg1.adjacent(pg2): - combined = pin_group(pin_name, [], self) - combined.combine_groups(pg1, pg2) - debug.info(3,"Combining {0} {1} {2}:".format(pin_name, index1, index2)) - debug.info(3, " {0}\n {1}".format(pg1.pins, pg2.pins)) - debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) - remove_indices.update([index1,index2]) - pin_groups.append(combined) - break + if not index1 in adjacent_pins.keys(): + adjacent_pins[index1] = set([index2]) + else: + adjacent_pins[index1].add(index2) - # Remove them in decreasing order to not invalidate the indices - debug.info(4,"Removing {}".format(sorted(remove_indices))) - for i in sorted(remove_indices, reverse=True): - del pin_groups[i] - - # Use the new pin group! - self.pin_groups[pin_name] = pin_groups + # Make a list of indices to ensure every group gets in the new set + all_indices = set([x for x in range(len(self.pin_groups[pin_name]))]) - removed_pairs = int(len(remove_indices)/2) - debug.info(1, "Combined {0} pin pairs for {1}".format(removed_pairs,pin_name)) + # Now reconstruct the new groups + new_pin_groups = [] + for index1,index2_set in adjacent_pins.items(): + # Remove the indices if they are added to the new set + all_indices.discard(index1) + all_indices.difference_update(index2_set) + + # Create the combined group starting with the first item + combined = self.pin_groups[pin_name][index1] + # Add all of the other items that overlapped + for index2 in index2_set: + pg = self.pin_groups[pin_name][index2] + combined.add_group(pg) + debug.info(3,"Combining {0} {1}:".format(pin_name, index2)) + debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins)) + debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) + new_pin_groups.append(combined) + + # Add the pin groups that weren't added to the new set + for index in all_indices: + new_pin_groups.append(self.pin_groups[pin_name][index]) + + old_size = len(self.pin_groups[pin_name]) + # Use the new pin group! + self.pin_groups[pin_name] = new_pin_groups + removed_pairs = old_size - len(new_pin_groups) + debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) return removed_pairs - def combine_adjacent_pins(self, pin_name): - """ - Make multiple passes of the combine adjacent pins until we have no - more combinations or hit an iteration limit. - """ - debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) - # Start as None to signal the first iteration - num_removed_pairs = None - - # Just used in case there's a circular combination or something weird - for iteration_count in range(10): - num_removed_pairs = self.combine_adjacent_pins_pass(pin_name) - if num_removed_pairs==0: - break - else: - debug.warning("Did not converge combining adjacent pins in supply router.") def separate_adjacent_pins(self, separation): """ @@ -271,7 +279,7 @@ class router(router_tech): debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2)) for index1,pg1 in enumerate(self.pin_groups[pin_name1]): for index2,pg2 in enumerate(self.pin_groups[pin_name2]): - # FIXME: Use separation distance and edge grids only + # FIgXME: Use separation distance and edge grids only grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation) # These should have the same length, so... if len(grids_g1)>0: diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 00ea30bc..a56b1a42 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -2,13 +2,14 @@ import gdsMill import tech import math import debug -from globals import OPTS +from globals import OPTS,print_time from contact import contact from pin_group import pin_group from pin_layout import pin_layout from vector3d import vector3d from router import router from direction import direction +from datetime import datetime import grid import grid_utils @@ -68,10 +69,13 @@ class supply_router(router): self.compute_supply_rail_dimensions() # Get the pin shapes + #start_time = datetime.now() self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) + #print_time("Pins and blockages",datetime.now(), start_time) #self.write_debug_gds("pin_enclosures.gds",stop_program=True) # Add the supply rails in a mesh network and connect H/V with vias + #start_time = datetime.now() # Block everything self.prepare_blockages(self.gnd_name) # Determine the rail locations @@ -82,15 +86,20 @@ class supply_router(router): # Determine the rail locations self.route_supply_rails(self.vdd_name,1) #self.write_debug_gds("debug_rails.gds",stop_program=True) - + #print_time("Supply rails",datetime.now(), start_time) + + #start_time = datetime.now() self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(gnd_name) + #print_time("Simple overlaps",datetime.now(), start_time) #self.write_debug_gds("debug_simple_route.gds",stop_program=False) # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter + #start_time = datetime.now() self.route_pins_to_rails(vdd_name) self.route_pins_to_rails(gnd_name) + #print_time("Routing",datetime.now(), start_time) #self.write_debug_gds("debug_pin_routes.gds",stop_program=True) #self.write_debug_gds("final.gds",False)