From 8b3c10ae796dcf17aa23d392b4db19b6b1dbc895 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 4 Mar 2022 15:44:07 -0800 Subject: [PATCH] Improvements to power routing. Improved the route horizontal and vertical pin functions to create a single pin at the end. Swapped A and B on wordline driver input for cleaner routing in most technologies. Fixed vertical supply routing in port_address. --- compiler/base/hierarchy_layout.py | 96 +++++++++++++++-------- compiler/modules/hierarchical_decoder.py | 12 +-- compiler/modules/port_address.py | 12 ++- compiler/modules/replica_bitcell_array.py | 5 ++ compiler/modules/wordline_driver_array.py | 26 +++--- 5 files changed, 92 insertions(+), 59 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index ff0d7a3d..93d2de51 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -74,24 +74,30 @@ class layout(): ############################################################ # GDS layout ############################################################ - def offset_all_coordinates(self): + def offset_all_coordinates(self, offset=None): """ This function is called after everything is placed to shift the origin in the lowest left corner """ - offset = self.find_lowest_coords() - self.translate_all(offset) - return offset + if not offset: + offset = vector(0, 0) + ll = self.find_lowest_coords() + real_offset = ll + offset + self.translate_all(real_offset) + return real_offset - def offset_x_coordinates(self): + def offset_x_coordinates(self, offset=None): """ This function is called after everything is placed to shift the origin to the furthest left point. Y offset is unchanged. """ - offset = self.find_lowest_coords() - self.translate_all(offset.scale(1, 0)) - return offset + if not offset: + offset = vector(0, 0) + ll = self.find_lowest_coords() + real_offset = ll.scale(1, 0) + offset + self.translate_all(real_offset) + return real_offset def get_gate_offset(self, x_offset, height, inv_num): """ @@ -422,7 +428,7 @@ class layout(): for pin_name in self.pin_map.keys(): self.copy_layout_pin(instance, pin_name, prefix + pin_name) - def route_vertical_pins(self, name, insts=None, layer=None, side=None): + def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"): """ Route together all of the pins of a given name that vertically align. Uses local_insts if insts not specified. @@ -437,19 +443,17 @@ class layout(): for inst in insts: for pin in inst.get_pins(name): - if side == "right": - x = pin.rx() - elif side == "left": - x = pin.lx() - else: - x = pin.cx() - + x = getattr(pin, xside)() try: bins[x].append((inst,pin)) except KeyError: bins[x] = [(inst,pin)] for x, v in bins.items(): + # Not enough to route a pin + if len(v) < 2: + continue + bot_y = min([inst.by() for (inst,pin) in v]) top_y = max([inst.uy() for (inst,pin) in v]) @@ -459,9 +463,12 @@ class layout(): pin_layer = layer else: pin_layer = self.supply_stack[2] + + y = getattr(pin, yside)() + last_via = self.add_via_stack_center(from_layer=pin.layer, to_layer=pin_layer, - offset=vector(x, pin.cy()), + offset=vector(x, y), min_area=True) if last_via: @@ -469,13 +476,22 @@ class layout(): else: via_width=None - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=vector(x, bot_y), - end=vector(x, top_y), - width=via_width) + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + self.add_segment_center(layer=pin_layer, + start=bot_pos, + end=top_pos, + width=via_width) - def route_horizontal_pins(self, name, insts=None, layer=None, side=None): + self.add_layout_pin_rect_center(text=name, + layer=pin_layer, + offset=top_pos) +# self.add_layout_pin_rect_center(text=name, +# layer=pin_layer, +# offset=bot_pos) + + + def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"): """ Route together all of the pins of a given name that horizontally align. Uses local_insts if insts not specified. @@ -489,19 +505,20 @@ class layout(): for inst in insts: for pin in inst.get_pins(name): - if side == "top": - y = pin.uy() - elif side == "bottom": - y = pin.by() - else: - y = pin.cy() + y = getattr(pin, yside)() try: bins[y].append((inst,pin)) except KeyError: bins[y] = [(inst,pin)] + # Filter the small bins + for y, v in bins.items(): + # Not enough to route a pin + if len(v) < 2: + continue + left_x = min([inst.lx() for (inst,pin) in v]) right_x = max([inst.rx() for (inst,pin) in v]) @@ -511,6 +528,9 @@ class layout(): pin_layer = layer else: pin_layer = self.supply_stack[0] + + x = getattr(pin, xside)() + last_via = self.add_via_stack_center(from_layer=pin.layer, to_layer=pin_layer, offset=vector(pin.cx(), y), @@ -521,11 +541,19 @@ class layout(): else: via_height=None - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=vector(left_x, y), - end=vector(right_x, y), - width=via_height) + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + self.add_segment_center(layer=pin_layer, + start=left_pos, + end=right_pos, + width=via_height) + + self.add_layout_pin_rect_center(text=name, + layer=pin_layer, + offset=left_pos) +# self.add_layout_pin_rect_center(text=name, +# layer=pin_layer, +# offset=right_pos) def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 7bddb3a3..e9f8bbd8 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -596,10 +596,10 @@ class hierarchical_decoder(design.design): """ if layer_props.hierarchical_decoder.vertical_supply: pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst - self.route_vertical_pins("vdd", insts=pre_insts) - self.route_vertical_pins("gnd", insts=pre_insts) - self.route_vertical_pins("vdd", insts=self.and_inst) - self.route_vertical_pins("gnd", insts=self.and_inst) + self.route_vertical_pins("vdd", insts=pre_insts, yside="by") + self.route_vertical_pins("gnd", insts=pre_insts, yside="by") + self.route_vertical_pins("vdd", insts=self.and_inst, yside="by") + self.route_vertical_pins("gnd", insts=self.and_inst, yside="by") return for n in ["vdd", "gnd"]: pins = self.and_inst[0].get_pins(n) @@ -622,8 +622,8 @@ class hierarchical_decoder(design.design): pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst self.route_vertical_pins("vdd", insts=pre_insts) self.route_vertical_pins("gnd", insts=pre_insts) - self.route_vertical_pins("vdd", insts=self.and_inst, side="right") - self.route_vertical_pins("gnd", insts=self.and_inst, side="left") + self.route_vertical_pins("vdd", insts=self.and_inst, xside="rx") + self.route_vertical_pins("gnd", insts=self.and_inst, xside="lx") # Widen the rails to cover any gap for inst in self.and_inst: diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index e06d6857..5e0b74fa 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -228,17 +228,15 @@ class port_address(design.design): wordline_driver_array_offset = vector(self.row_decoder_inst.rx(), 0) self.wordline_driver_array_inst.place(wordline_driver_array_offset) - # The wordline driver also had an extra gap on the right, so use this offset - well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - x_offset = self.wordline_driver_array_inst.rx() - well_gap - self.rbl_driver.width + # This m4_pitch corresponds to the offset space for jog routing in the + # wordline_driver_array + rbl_driver_offset = wordline_driver_array_offset + vector(self.m4_pitch, 0) if self.port == 0: - rbl_driver_offset = vector(x_offset, - 0) self.rbl_driver_inst.place(rbl_driver_offset, "MX") else: - rbl_driver_offset = vector(x_offset, - self.wordline_driver_array.height) + rbl_driver_offset += vector(0, + self.wordline_driver_array.height) self.rbl_driver_inst.place(rbl_driver_offset) # Pass this up diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index a459c266..88701161 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -463,6 +463,11 @@ class replica_bitcell_array(bitcell_base_array): supply_insts = self.dummy_col_insts + self.dummy_row_insts for pin_name in self.supplies: + #self.route_vertical_pins(name=pin_name, insts=supply_insts) + self.route_horizontal_pins(name=pin_name, insts=supply_insts) + + #self.route_vertical_pins(name=pin_name, insts=self.replica_col_insts) + #self.route_horizontal_pins(name=pin_name, insts=self.replica_col_insts) for inst in supply_insts: pin_list = inst.get_pins(pin_name) for pin in pin_list: diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index 95a7d007..31b60887 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -42,9 +42,16 @@ class wordline_driver_array(design.design): self.route_layer = "li" else: self.route_layer = "m1" + self.place_drivers() self.route_layout() - self.offset_x_coordinates() + self.offset_x_coordinates(vector(-self.m4_pitch, 0)) + + # Leave a well gap to separate the bitcell array well from this well + well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") + self.width = self.wld_inst[-1].rx() + well_gap + self.height = self.wld_inst[-1].uy() + self.add_boundary() self.route_vdd_gnd() self.DRC_LVS() @@ -73,8 +80,8 @@ class wordline_driver_array(design.design): self.route_vertical_pins("vdd", insts=self.wld_inst) self.route_vertical_pins("gnd", insts=self.wld_inst) else: - self.route_vertical_pins("vdd", insts=self.wld_inst, side="left") - self.route_vertical_pins("gnd", insts=self.wld_inst, side="right") + self.route_vertical_pins("vdd", insts=self.wld_inst, xside="lx") + self.route_vertical_pins("gnd", insts=self.wld_inst, xside="rx") # Widen the rails to cover any gap for num in range(self.rows): @@ -93,8 +100,8 @@ class wordline_driver_array(design.design): # add and2 self.wld_inst.append(self.add_inst(name=name_and, mod=self.wl_driver)) - self.connect_inst(["en", - "in_{0}".format(row), + self.connect_inst(["in_{0}".format(row), + "en", "wl_{0}".format(row), "vdd", "gnd"]) @@ -114,18 +121,13 @@ class wordline_driver_array(design.design): self.wld_inst[row].place(offset=and2_offset, mirror=inst_mirror) - # Leave a well gap to separate the bitcell array well from this well - well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - self.width = self.wl_driver.width + well_gap - self.height = self.wl_driver.height * self.rows - def route_layout(self): """ Route all of the signals """ # Wordline enable connection en_pin = self.wld_inst[0].get_pin("B") en_bottom_pos = vector(en_pin.cx(), 0) - en_top_pos = vector(en_pin.cx(), self.height) + en_top_pos = vector(en_pin.cx(), self.wld_inst[-1].uy()) en_pin = self.add_layout_pin_segment_center(text="en", layer="m2", start=en_bottom_pos, @@ -135,7 +137,7 @@ class wordline_driver_array(design.design): and_inst = self.wld_inst[row] # Drop a via - b_pin = and_inst.get_pin("A") + b_pin = and_inst.get_pin("B") self.add_via_stack_center(from_layer=b_pin.layer, to_layer="m2", offset=b_pin.center())