From b1bb9151c41d5f2704e328d212dfe95537f3f906 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 2 May 2022 15:43:14 -0700 Subject: [PATCH] Reimplement off grid pins. Long pins aren't accessed on end pins anymore. Fix problem with multiple non-enclosed space causing blockages. Add partial pin offgrid enclosure algorithm. --- compiler/base/hierarchy_layout.py | 73 +++++------ compiler/base/pin_layout.py | 7 ++ compiler/modules/bank.py | 34 ++--- compiler/modules/control_logic.py | 42 ++++--- compiler/modules/delay_chain.py | 22 +--- compiler/modules/hierarchical_decoder.py | 12 +- compiler/modules/hierarchical_predecode.py | 2 +- compiler/modules/local_bitcell_array.py | 5 +- compiler/modules/port_address.py | 15 +-- compiler/modules/precharge_array.py | 2 +- compiler/modules/replica_bitcell_array.py | 49 +++++--- compiler/modules/wordline_driver_array.py | 11 +- compiler/router/pin_group.py | 6 +- compiler/router/router.py | 139 ++++++++++++++++++--- compiler/router/supply_tree_router.py | 6 +- compiler/sram/sram_base.py | 2 +- compiler/tests/configs/config.py | 2 +- 17 files changed, 274 insertions(+), 155 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index a37ccb2b..5c986d75 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -419,13 +419,6 @@ class layout(): pin.width(), pin.height()) - def copy_layout_pins(self, instance, prefix=""): - """ - Create a copied version of the layout pin at the current level. - You can optionally rename the pin to a new name. - """ - for pin_name in self.pin_map.keys(): - self.copy_layout_pin(instance, pin_name, prefix + pin_name) def connect_row_locs(self, from_layer, to_layer, locs, name=None, full=False): """ @@ -608,12 +601,11 @@ class layout(): - def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, full_width=True): + def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): """ Route together all of the pins of a given name that vertically align. Uses local_insts if insts not specified. Uses center of pin by default, or right or left if specified. - num_pins specifies whether to add a single pin or multiple pins (equally spaced) TODO: Add equally spaced option for IR drop min, right now just 2 """ @@ -660,8 +652,10 @@ class layout(): if last_via: via_width=last_via.mod.second_layer_width + via_height=last_via.mod.second_layer_height else: via_width=None + via_height=0 if full_width: bot_y = 0 @@ -669,21 +663,19 @@ class layout(): else: bot_y = min([pin.by() for (inst,pin) in v]) top_y = max([pin.uy() for (inst,pin) in v]) - top_pos = vector(x, top_y) - bot_pos = vector(x, bot_y) + top_pos = vector(x, top_y + 0.5 * via_height) + bot_pos = vector(x, bot_y - 0.5 * via_height) - if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=top_pos, - end=bot_pos, - width=via_width) - else: - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=top_pos, - end=bot_pos, - width=via_width) +# self.add_layout_pin_rect_ends(name=name, +# layer=pin_layer, +# start=top_pos, +# end=bot_pos, +# width=via_width) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=top_pos, + end=bot_pos, + width=via_width) def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): @@ -707,12 +699,13 @@ class layout(): start=bot_rect.rc(), end=top_rect.lc()) - def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, full_width=True): + return (bot_rect, top_rect) + + def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True): """ Route together all of the pins of a given name that horizontally align. Uses local_insts if insts not specified. Uses center of pin by default, or top or botom if specified. - num_pins specifies whether to add a single pin or multiple pins (equally spaced) TODO: Add equally spaced option for IR drop min, right now just 2 """ @@ -760,8 +753,10 @@ class layout(): if last_via: via_height=last_via.mod.second_layer_height + via_width=last_via.mod.second_layer_width else: via_height=None + via_width=0 if full_width: left_x = 0 @@ -769,22 +764,19 @@ class layout(): else: left_x = min([pin.lx() for (inst,pin) in v]) right_x = max([pin.rx() for (inst,pin) in v]) - left_pos = vector(left_x, y) - right_pos = vector(right_x, y) + left_pos = vector(left_x + 0.5 * via_width, y) + right_pos = vector(right_x + 0.5 * via_width, y) - if num_pins==2: - self.add_layout_pin_rect_ends(name=name, - layer=pin_layer, - start=left_pos, - end=right_pos, - width=via_height) - else: - # This adds a single big pin - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=left_pos, - end=right_pos, - width=via_height) +# self.add_layout_pin_rect_ends(name=name, +# layer=pin_layer, +# start=left_pos, +# end=right_pos, +# width=via_height) + self.add_layout_pin_segment_center(text=name, + layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) def add_layout_end_pin_segment_center(self, text, layer, start, end): """ @@ -1123,7 +1115,6 @@ class layout(): Add a minimum area retcangle at the given point. Either width or height should be fixed. """ - min_area = drc("minarea_{}".format(layer)) if min_area == 0: return diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index cc13e049..7ba6cdca 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -174,6 +174,10 @@ class pin_layout: def intersection(self, other): """ Check if a shape overlaps with a rectangle """ + + if not self.overlaps(other): + return None + (ll, ur) = self.rect (oll, our) = other.rect @@ -182,6 +186,9 @@ class pin_layout: min_y = max(ll.y, oll.y) max_y = min(ur.y, our.y) + if max_x - min_x == 0 or max_y - min_y == 0: + return None + return [vector(min_x, min_y), vector(max_x, max_y)] def xoverlaps(self, other): diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index bd3fc0a4..718f8f62 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -229,7 +229,7 @@ class bank(design.design): # UPPER LEFT QUADRANT # To the left of the bitcell array above the predecoders and control logic - x_offset = self.m2_gap + self.port_address[port].width + x_offset = self.decoder_gap + self.port_address[port].width self.port_address_offsets[port] = vector(-x_offset, self.main_bitcell_array_bottom) @@ -272,7 +272,7 @@ class bank(design.design): # LOWER RIGHT QUADRANT # To the right of the bitcell array - x_offset = self.bitcell_array_right + self.port_address[port].width + self.m2_gap + x_offset = self.bitcell_array_right + self.port_address[port].width + self.decoder_gap self.port_address_offsets[port] = vector(x_offset, self.main_bitcell_array_bottom) @@ -364,9 +364,9 @@ class bank(design.design): self.num_col_addr_lines = 0 self.col_addr_bus_width = self.m2_pitch * self.num_col_addr_lines - # A space for wells or jogging m2 - self.m2_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), - 3 * self.m2_pitch, + # Gap between decoder and array + self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"), + 2 * self.m2_pitch, drc("nwell_to_nwell")) def add_modules(self): @@ -400,11 +400,10 @@ class bank(design.design): self.port_data = [] self.bit_offsets = self.get_column_offsets() for port in self.all_ports: - temp_pre = factory.create(module_type="port_data", - sram_config=self.sram_config, - port=port, - bit_offsets=self.bit_offsets) - self.port_data.append(temp_pre) + self.port_data.append(factory.create(module_type="port_data", + sram_config=self.sram_config, + port=port, + bit_offsets=self.bit_offsets)) if(self.num_banks > 1): self.bank_select = factory.create(module_type="bank_select") @@ -606,15 +605,22 @@ class bank(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ + # Copy only the power pins already on the power layer # (this won't add vias to internal bitcell pins, for example) - for inst in self.insts: - self.copy_power_pins(inst, "vdd", add_vias=False) - self.copy_power_pins(inst, "gnd", add_vias=False) + + # This avoids getting copy errors on vias and other instances + all_insts = [self.bitcell_array_inst] + self.port_address_inst + self.port_data_inst + if hasattr(self, "column_decoder_inst"): + all_insts += self.column_decoder_inst + + for inst in all_insts: + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins: for pin_name, supply_name in zip(['vnb','vpb'],['gnd','vdd']): - self.copy_power_pins(self.bitcell_array_inst, pin_name, new_name=supply_name) + self.copy_layout_pin(self.bitcell_array_inst, pin_name, new_name=supply_name) # If we use the pinvbuf as the decoder, we need to add power pins. # Other decoders already have them. diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index df7a8241..79ce3e30 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -715,12 +715,17 @@ class control_logic(design.design): pin_layer = self.dff.get_pin("vdd").layer supply_layer = self.supply_stack[2] + + # FIXME: We should be able to replace this with route_vertical_pins instead + # but we may have to make the logic gates a separate module so that they + # have row pins of the same width max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) min_row_x_loc = self.control_x_offset vdd_pin_locs = [] gnd_pin_locs = [] + last_via = None for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: @@ -728,10 +733,10 @@ class control_logic(design.design): row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) vdd_pin_locs.append(pin_loc) - self.add_via_stack_center(from_layer=pin_layer, - to_layer=supply_layer, - offset=pin_loc, - min_area=True) + last_via = self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) self.add_path(pin_layer, [row_loc, pin_loc]) pins = inst.get_pins("gnd") @@ -740,29 +745,38 @@ class control_logic(design.design): row_loc = pin.rc() pin_loc = vector(min_row_x_loc, pin.rc().y) gnd_pin_locs.append(pin_loc) - self.add_via_stack_center(from_layer=pin_layer, - to_layer=supply_layer, - offset=pin_loc, - min_area=True) + last_via = self.add_via_stack_center(from_layer=pin_layer, + to_layer=supply_layer, + offset=pin_loc, + min_area=True) self.add_path(pin_layer, [row_loc, pin_loc]) + + if last_via: + via_height=last_via.mod.second_layer_height + via_width=last_via.mod.second_layer_width + else: + via_height=None + via_width=0 min_y = min([x.y for x in vdd_pin_locs]) max_y = max([x.y for x in vdd_pin_locs]) - bot_pos = vector(max_row_x_loc, min_y) - top_pos = vector(max_row_x_loc, max_y) + bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height) + top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height) self.add_layout_pin_segment_center(text="vdd", layer=supply_layer, start=bot_pos, - end=top_pos) + end=top_pos, + width=via_width) min_y = min([x.y for x in gnd_pin_locs]) max_y = max([x.y for x in gnd_pin_locs]) - bot_pos = vector(min_row_x_loc, min_y) - top_pos = vector(min_row_x_loc, max_y) + bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height) + top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height) self.add_layout_pin_segment_center(text="gnd", layer=supply_layer, start=bot_pos, - end=top_pos) + end=top_pos, + width=via_width) self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "vdd") diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index af446bbd..fe039411 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -172,25 +172,9 @@ class delay_chain(design.design): self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()]) def route_supplies(self): - # Add power and ground to all the cells except: - # the fanout driver, the right-most load - # The routing to connect the loads is over the first and last cells - # We have an even number of drivers and must only do every other - # supply rail - if True or OPTS.experimental_power: - left_load_insts = [self.load_inst_map[x][0] for x in self.driver_inst_list] - right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] - self.route_vertical_pins("vdd", left_load_insts, xside="lx") - self.route_vertical_pins("gnd", right_load_insts, xside="rx") - else: - for inst in self.driver_inst_list: - load_list = self.load_inst_map[inst] - for pin_name in ["vdd", "gnd"]: - pin = load_list[0].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) - - pin = load_list[-2].get_pin(pin_name) - self.copy_power_pin(pin, loc=pin.rc() - vector(self.m1_pitch, 0)) + self.route_vertical_pins("vdd", self.driver_inst_list, xside="lx") + right_load_insts = [self.load_inst_map[x][-1] for x in self.driver_inst_list] + self.route_vertical_pins("gnd", right_load_insts, xside="rx") def add_layout_pins(self): diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 2b173969..40c2a93a 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -595,14 +595,12 @@ class hierarchical_decoder(design.design): # Leave these to route in the port_address all_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst for inst in all_insts: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") + self.copy_layout_pin(inst, "vdd") + self.copy_layout_pin(inst, "gnd") + + self.route_vertical_pins("vdd", self.and_inst, xside="rx",) + self.route_vertical_pins("gnd", self.and_inst, xside="lx",) - for inst in self.and_inst: - for pin in inst.get_pins("vdd"): - self.add_power_pin("vdd", pin.rc()) - for pin in inst.get_pins("gnd"): - self.add_power_pin("gnd", pin.lc()) def route_predecode_bus_outputs(self, rail_name, pin, row): diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 0d7f31a3..8ae3a08d 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -395,7 +395,7 @@ class hierarchical_predecode(design.design): height=top_pin.uy() - self.bus_pitch) # This adds power vias at the top of each cell # (except the last to keep them inside the boundary) - for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]: + for i in [self.inv_inst[0], self.inv_inst[-2], self.and_inst[0], self.and_inst[-2]]: pins = i.get_pins(n) for pin in pins: self.copy_power_pin(pin, loc=pin.uc()) diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 49e8ed76..0d3cfd12 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -162,14 +162,15 @@ class local_bitcell_array(bitcell_base_array.bitcell_base_array): # FIXME: Replace this with a tech specific paramter driver_to_array_spacing = 3 * self.m3_pitch - self.wl_insts[0].place(vector(0, self.cell.height)) + self.wl_insts[0].place(vector(0, + self.bitcell_array.get_replica_bottom() + self.cell.height)) self.bitcell_array_inst.place(vector(self.wl_insts[0].rx() + driver_to_array_spacing, 0)) if len(self.all_ports) > 1: self.wl_insts[1].place(vector(self.bitcell_array_inst.rx() + self.wl_array.width + driver_to_array_spacing, - 2 * self.cell.height + self.wl_array.height), + self.bitcell_array.get_replica_top() + self.cell.height), mirror="XY") self.height = self.bitcell_array.height diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 2757a86f..a96a556e 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -76,22 +76,19 @@ class port_address(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - self.route_vertical_pins("vdd", [self.row_decoder_inst]) - self.route_vertical_pins("gnd", [self.row_decoder_inst]) - self.route_vertical_pins("vdd", [self.wordline_driver_array_inst]) if layer_props.wordline_driver.vertical_supply: - self.route_vertical_pins("gnd", [self.wordline_driver_array_inst]) self.copy_layout_pin(self.rbl_driver_inst, "vdd") else: rbl_pos = self.rbl_driver_inst.get_pin("vdd").rc() self.add_power_pin("vdd", rbl_pos) self.add_path("m4", [rbl_pos, self.wordline_driver_array_inst.get_pins("vdd")[0].rc()]) - - vdd_pins = self.row_decoder_inst.get_pins("vdd") + self.wordline_driver_array_inst.get_pins("vdd") - self.connect_row_pins(self.route_layer, vdd_pins) - gnd_pins = self.row_decoder_inst.get_pins("gnd") + self.wordline_driver_array_inst.get_pins("gnd") - self.connect_row_pins(self.route_layer, gnd_pins) + self.copy_layout_pin(self.wordline_driver_array_inst, "vdd") + self.copy_layout_pin(self.wordline_driver_array_inst, "gnd") + + self.copy_layout_pin(self.row_decoder_inst, "vdd") + self.copy_layout_pin(self.row_decoder_inst, "gnd") + # Also connect the B input of the RBL and_dec to vdd if OPTS.local_array_size == 0: rbl_b_pin = self.rbl_driver_inst.get_pin("B") diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index c820dfc1..372a5629 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -83,7 +83,7 @@ class precharge_array(design.design): def add_layout_pins(self): en_pin = self.pc_cell.get_pin("en_bar") - self.route_horizontal_pins("en_bar", layer=self.en_bar_layer, num_pins=1) + self.route_horizontal_pins("en_bar", layer=self.en_bar_layer) for inst in self.local_insts: self.add_via_stack_center(from_layer=en_pin.layer, to_layer=self.en_bar_layer, diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index e1a3cbb8..785bb62b 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -331,6 +331,7 @@ class replica_bitcell_array(bitcell_base_array): self.translate_all(array_offset.scale(-1, -1)) # Add extra width on the left and right for the unused WLs + self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x self.height = self.dummy_row_insts[1].uy() @@ -340,6 +341,12 @@ class replica_bitcell_array(bitcell_base_array): self.route_unused_wordlines() + lower_left = self.find_lowest_coords() + upper_right = self.find_highest_coords() + self.width = upper_right.x - lower_left.x + self.height = upper_right.y - lower_left.y + self.translate_all(lower_left) + self.add_boundary() self.DRC_LVS() @@ -356,6 +363,18 @@ class replica_bitcell_array(bitcell_base_array): def get_main_array_right(self): return self.bitcell_array_inst.rx() + def get_replica_top(self): + return max([x.uy() for x in self.replica_col_insts if x] + [self.get_main_array_top()]) + + def get_replica_bottom(self): + return min([x.by() for x in self.replica_col_insts if x] + [self.get_main_array_bottom()]) + + def get_replica_left(self): + return min([x.lx() for x in self.replica_col_insts if x] + [self.get_main_array_left()]) + + def get_replica_right(self): + return min([x.rx() for x in self.replica_col_insts if x] + [self.get_main_array_right()]) + def get_column_offsets(self): """ Return an array of the x offsets of all the regular bits @@ -567,14 +586,15 @@ class replica_bitcell_array(bitcell_base_array): top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height) layer = self.supply_stack[2] - self.add_path(layer, [bot_loc, top_loc]) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=top_loc) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=bot_loc) +# self.add_layout_pin_rect_ends(text=name, +# layer=layer, +# start=bot_loc, +# end=top_loc) + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=bot_loc, + end=top_loc) return (bot_loc, top_loc) @@ -590,14 +610,15 @@ class replica_bitcell_array(bitcell_base_array): right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch) layer = self.supply_stack[0] - self.add_path(layer, [left_loc, right_loc]) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=left_loc) - self.add_layout_pin_rect_center(text=name, - layer=layer, - offset=right_loc) +# self.add_layout_pin_rect_ends(text=name, +# layer=layer, +# start=left_loc, +# end=right_loc) + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=left_loc, + end=right_loc) return (left_loc, right_loc) diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index 276194ee..63c39a7c 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -77,11 +77,12 @@ class wordline_driver_array(design.design): Add vertical power rails. """ - for inst in self.wld_inst: - for pin in inst.get_pins("vdd"): - self.add_power_pin("vdd", pin.rc()) - #self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") + if layer_props.wordline_driver.vertical_supply: + self.route_vertical_pins("vdd", self.wld_inst) + self.route_vertical_pins("gnd", self.wld_inst) + else: + self.route_vertical_pins("vdd", self.wld_inst, xside="rx",) + self.route_vertical_pins("gnd", self.wld_inst, xside="lx",) def create_drivers(self): diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 5e6d6f89..d67200d5 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -158,7 +158,7 @@ class pin_group: # Now add the right name for pin in new_pin_list: pin.name = self.name - + debug.check(len(new_pin_list) > 0, "Did not find any enclosures.") @@ -424,7 +424,7 @@ class pin_group: debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.") common_blockages = self.router.get_blocked_grids() & self.grids - + # Start with the ll and make the widest row row = [ll] # Move in dir1 while we can @@ -641,7 +641,7 @@ class pin_group: # way than blockages. blockages = sufficient | insufficient | blockage_in_tracks self.blockages.update(blockages) - + # If we have a blockage, we must remove the grids # Remember, this excludes the pin blockages already blocked_grids = self.router.get_blocked_grids() diff --git a/compiler/router/router.py b/compiler/router/router.py index fd00d98d..94a4c432 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -229,9 +229,9 @@ class router(router_tech): # Enclose the continguous grid units in a metal # rectangle to fix some DRCs - start_time = datetime.now() - self.enclose_pins() - print_time("Enclosing pins", datetime.now(), start_time, 4) + #start_time = datetime.now() + #self.enclose_pins() + #print_time("Enclosing pins", datetime.now(), start_time, 4) # MRG: Removing this code for now. The later compute enclosure code # assumes that all pins are touching and this may produce sets of pins @@ -670,6 +670,25 @@ class router(router_tech): return set([best_coord]) + def divide_pin_to_tracks(self, pin, tracks): + """ + Return a list of pin shape parts that are in the tracks. + """ + overlap_pins = [] + for track in tracks: + track_pin = self.convert_track_to_shape_pin(track) + overlap_rect = track_pin.intersection(pin) + if not overlap_rect: + continue + else: + overlap_pin = pin_layout(pin.name, + overlap_rect, + pin.layer) + overlap_pins.append(overlap_pin) + + return overlap_pins + + def convert_pin_coord_to_tracks(self, pin, coord): """ Return all tracks that an inflated pin overlaps @@ -893,6 +912,8 @@ class router(router_tech): This will mark the grids for all pin components as a source. Marking as source or target also clears blockage status. """ + self.source_name = pin_name + self.source_components = [] for i in range(self.num_pin_components(pin_name)): self.add_pin_component_source(pin_name, i) @@ -904,6 +925,8 @@ class router(router_tech): This will mark the grids for all pin components as a target. Marking as source or target also clears blockage status. """ + self.target_name = pin_name + self.target_components = [] for i in range(self.num_pin_components(pin_name)): self.add_pin_component_target(pin_name, i) @@ -1009,6 +1032,8 @@ class router(router_tech): """ This will mark all the cells on the perimeter of the original layout as a target. """ + self.target_name = "" + self.target_components = [] self.rg.add_perimeter_target(side=side) def num_pin_components(self, pin_name): @@ -1017,6 +1042,15 @@ class router(router_tech): """ return len(self.pin_groups[pin_name]) + def set_pin_component_source(self, pin_name, index): + """ + Add the pin component but also set it as the exclusive one. + Used by supply routing with multiple components. + """ + self.source_name = pin_name + self.source_components = [] + self.add_pin_component_source(pin_name, index) + def add_pin_component_source(self, pin_name, index): """ This will mark only the pin tracks @@ -1026,6 +1060,7 @@ class router(router_tech): debug.check(index 1: + self.cell.add_route(layers=self.layers, + coordinates=abs_path, + layer_widths=self.layer_widths) else: - # convert the path back to absolute units from tracks - # This assumes 1-track wide again - abs_path = [self.convert_point_to_units(x[0]) for x in path] - # Otherwise, add the route which includes enclosures - if len(self.layers) > 1: - self.cell.add_route(layers=self.layers, - coordinates=abs_path, - layer_widths=self.layer_widths) - else: - self.cell.add_path(layer=self.layers[0], - coordinates=abs_path, - width=self.layer_widths[0]) + self.cell.add_path(layer=self.layers[0], + coordinates=abs_path, + width=self.layer_widths[0]) + + def create_route_connector(self, path_tracks, pin_name, components): + """ + Find a rectangle to connect the track and the off-grid pin of a component. + """ + + if len(path_tracks) == 0 or len(components) == 0: + return + + # Convert the off-grid pin into parts in each routing grid + offgrid_pin_parts = [] + for component in components: + pg = self.pin_groups[pin_name][component] + for pin in pg.pins: + partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids) + offgrid_pin_parts.extend(partial_pin_parts) + + # Find the track pin + track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks] + + # Find closest part + closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts) + + # Find the bbox of the on-grid track and the off-grid pin part + closest_track_pin.bbox([closest_part_pin]) + + # Connect to off grid pin to track pin with closest shape + self.cell.add_rect(layer=closest_track_pin.layer, + offset=closest_track_pin.ll(), + width=closest_track_pin.width(), + height=closest_track_pin.height()) + + def find_closest_pin(self, first_list, second_list): + """ + Find the closest pin in the lists. Does a stupid O(n^2). + """ + min_dist = None + min_item = None + for pin1 in first_list: + for pin2 in second_list: + if pin1.layer != pin2.layer: + continue + new_dist = pin1.distance(pin2) + if not min_item or new_dist < min_dist: + min_item = (pin1, pin2) + min_dist = new_dist + + return min_item + def add_single_enclosure(self, track): """ @@ -1192,7 +1283,15 @@ class router(router_tech): self.paths.append(grid_utils.flatten_set(path)) self.add_route(path) + self.create_route_connector(path, + self.source_name, + self.source_components) + self.create_route_connector(path, + self.target_name, + self.target_components) self.path_blockages.append(self.paths[-1]) + #self.write_debug_gds("debug_route.gds", False) + #breakpoint() return True else: return False diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index 9450243d..6873999a 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -154,7 +154,7 @@ class supply_tree_router(router): print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)) self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) - #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) + self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) #return def route_signal(self, pin_name, src_idx, dest_idx): @@ -180,11 +180,11 @@ class supply_tree_router(router): # Add the single component of the pin as the source # which unmarks it as a blockage too - self.add_pin_component_source(pin_name, src_idx) + self.set_pin_component_source(pin_name, src_idx) # Marks all pin components except index as target # which unmarks it as a blockage too - self.add_pin_component_target(pin_name, dest_idx) + self.set_pin_component_target(pin_name, dest_idx) # Actually run the A* router if self.run_router(detour_scale=detour_scale): diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index f6782597..4bcc1cb3 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -213,7 +213,7 @@ class sram_base(design, verilog, lef): self.add_lvs_correspondence_points() - self.offset_all_coordinates() + #self.offset_all_coordinates() highest_coord = self.find_highest_coords() self.width = highest_coord[0] diff --git a/compiler/tests/configs/config.py b/compiler/tests/configs/config.py index b56c63db..44c3e774 100644 --- a/compiler/tests/configs/config.py +++ b/compiler/tests/configs/config.py @@ -14,6 +14,6 @@ tech_name = OPTS.tech_name nominal_corner_only = True check_lvsdrc = True -route_supplies = False +#route_supplies = False output_name = "sram"