diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 403920c6..6fca63c7 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -407,8 +407,8 @@ class layout(): """ pins = instance.get_pins(pin_name) - debug.check(len(pins) > 0, - "Could not find pin {}".format(pin_name)) + if len(pins) == 0: + debug.warning("Could not find pin {0} on {1}".format(pin_name, instance.mod.name)) for pin in pins: if new_name == "": @@ -427,11 +427,194 @@ 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, xside="cx", yside="cy"): + def connect_row_locs(self, from_layer, to_layer, locs, name=None, full=False): + """ + Connects left/right rows that are aligned on the given layer. + """ + bins = {} + for loc in locs: + y = pin.y + try: + bins[y].append(loc) + except KeyError: + bins[y] = [loc] + + for y, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + left_x = 0 + right_x = self.width + else: + left_x = min([loc.x for loc in v]) + right_x = max([loc.x for loc in v]) + + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + + # Make sure to add vias to the new route + for loc in v: + self.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=loc, + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=to_layer, + start=left_pos, + end=right_pos) + else: + self.add_segment_center(layer=to_layer, + start=left_pos, + end=right_pos) + + def connect_row_pins(self, layer, pins, name=None, full=False): + """ + Connects left/right rows that are aligned. + """ + bins = {} + for pin in pins: + y = pin.cy() + try: + bins[y].append(pin) + except KeyError: + bins[y] = [pin] + + for y, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + left_x = 0 + right_x = self.width + else: + left_x = min([pin.lx() for pin in v]) + right_x = max([pin.rx() for pin in v]) + + left_pos = vector(left_x, y) + right_pos = vector(right_x, y) + + # Make sure to add vias to the new route + for pin in v: + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin.center(), + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=left_pos, + end=right_pos) + else: + self.add_segment_center(layer=layer, + start=left_pos, + end=right_pos) + + def connect_col_locs(self, from_layer, to_layer, locs, name=None, full=False): + """ + Connects top/bot columns that are aligned. + """ + bins = {} + for loc in locs: + x = loc.x + try: + bins[x].append(loc) + except KeyError: + bins[x] = [loc] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + bot_y = 0 + top_y = self.height + else: + bot_y = min([loc.y for loc in v]) + top_y = max([loc.y for loc in v]) + + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + + # Make sure to add vias to the new route + for loc in v: + self.add_via_stack_center(from_layer=from_layer, + to_layer=to_layer, + offset=loc, + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=to_layer, + start=top_pos, + end=bot_pos) + else: + self.add_segment_center(layer=to_layer, + start=top_pos, + end=bot_pos) + + + def connect_col_pins(self, layer, pins, name=None, full=False): + """ + Connects top/bot columns that are aligned. + """ + bins = {} + for pin in pins: + x = pin.cx() + try: + bins[x].append(pin) + except KeyError: + bins[x] = [pin] + + for x, v in bins.items(): + # Not enough to route a pin, so just copy them + if len(v) < 2: + continue + + if full: + bot_y = 0 + top_y = self.height + else: + bot_y = min([pin.by() for pin in v]) + top_y = max([pin.uy() for pin in v]) + + top_pos = vector(x, top_y) + bot_pos = vector(x, bot_y) + + # Make sure to add vias to the new route + for pin in v: + self.add_via_stack_center(from_layer=pin.layer, + to_layer=layer, + offset=pin.center(), + min_area=True) + + if name: + self.add_layout_pin_segment_center(text=name, + layer=layer, + start=top_pos, + end=bot_pos) + else: + self.add_segment_center(layer=layer, + start=top_pos, + end=bot_pos) + + + + + + def route_vertical_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", num_pins=2, 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 """ @@ -451,7 +634,7 @@ class layout(): for x, v in bins.items(): # Not enough to route a pin, so just copy them if len(v) < 2: - debug.warning("Copying pins instead of connecting with pin.") + debug.warning("Pins don't align well so copying pins instead of connecting with pin.") for inst,pin in v: self.add_layout_pin(pin.name, pin.layer, @@ -460,10 +643,8 @@ class layout(): pin.height()) continue - bot_y = min([pin.by() for (inst,pin) in v]) - top_y = max([pin.uy() for (inst,pin) in v]) - last_via = None + pin_layer = None for inst,pin in v: if layer: pin_layer = layer @@ -482,31 +663,57 @@ class layout(): else: via_width=None + if full_width: + bot_y = 0 + top_y = self.height + 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_rect = self.add_layout_pin_rect_center(text=name, -# layer=pin_layer, -# offset=top_pos) - #bot_rect = self.add_layout_pin_rect_center(text=name, - # layer=pin_layer, - # offset=bot_pos) -# self.add_segment_center(layer=pin_layer, -# start=vector(top_rect.cx(), bot_pos.y), -# end=top_rect.bc(), -# width=via_width) - self.add_layout_pin_segment_center(text=name, - layer=pin_layer, - start=top_pos, - end=bot_pos, - width=via_width) + + 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) + def add_layout_pin_rect_ends(self, name, layer, start, end, width=None): - def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"): + # This adds pins on the end connected by a segment + top_rect = self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=start) + bot_rect = self.add_layout_pin_rect_center(text=name, + layer=layer, + offset=end) + # This is made to not overlap with the pin above + # so that the power router will only select a small pin. + # Otherwise it adds big blockages over the rails. + if start.y != end.y: + self.add_segment_center(layer=layer, + start=bot_rect.uc(), + end=top_rect.bc()) + else: + self.add_segment_center(layer=layer, + 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): """ 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 """ @@ -527,7 +734,7 @@ class layout(): for y, v in bins.items(): if len(v) < 2: - debug.warning("Copying pins instead of connecting with pin.") + debug.warning("Pins don't align well so copying pins instead of connecting with pin.") for inst,pin in v: self.add_layout_pin(pin.name, pin.layer, @@ -536,10 +743,8 @@ class layout(): pin.height()) continue - left_x = min([pin.lx() for (inst,pin) in v]) - right_x = max([pin.rx() for (inst,pin) in v]) - last_via = None + pin_layer = None for inst,pin in v: if layer: pin_layer = layer @@ -550,7 +755,7 @@ class layout(): last_via = self.add_via_stack_center(from_layer=pin.layer, to_layer=pin_layer, - offset=vector(pin.cx(), y), + offset=vector(x, y), min_area=True) if last_via: @@ -558,29 +763,56 @@ class layout(): else: via_height=None + if full_width: + left_x = 0 + right_x = self.width + 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_rect = self.add_layout_pin_rect_center(text=name, -# layer=pin_layer, -# offset=left_pos) - #right_rect = self.add_layout_pin_rect_center(text=name, - # layer=pin_layer, - # offset=right_pos) - # This is made to not overlap with the pin above - # so that the power router will only select a small pin. - # Otherwise it adds big blockages over the rails. -# self.add_segment_center(layer=pin_layer, -# start=left_rect.rc(), -# end=vector(right_pos.x, left_rect.cy()), -# width=via_height) + 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_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): + """ + Creates a path with two pins on the end that don't overlap. + """ + start_pin = self.add_layout_pin_rect_center(text=text, + layer=layer, + offset=start) + end_pin = self.add_layout_pin_rect_center(text=text, + layer=layer, + offset=end) + + if start.x != end.x and start.y != end.y: + file_name = "non_rectilinear.gds" + self.gds_write(file_name) + debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) + elif start.x != end.x: + self.add_segment_center(layer=layer, + start=start_pin.rc(), + end=end_pin.lc()) + elif start.y != end.y: + self.add_segment_center(layer=layer, + start=start_pin.uc(), + end=end_pin.bc()) + else: + debug.error("Cannot have a point pin.", -1) + def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ @@ -1447,12 +1679,13 @@ class layout(): width = None height = None + pin = None if start_layer in self.pwr_grid_layers: - self.add_layout_pin_rect_center(text=name, - layer=start_layer, - offset=loc, - width=width, - height=height) + pin = self.add_layout_pin_rect_center(text=name, + layer=start_layer, + offset=loc, + width=width, + height=height) else: via = self.add_via_stack_center(from_layer=start_layer, to_layer=self.pwr_grid_layers[0], @@ -1463,11 +1696,13 @@ class layout(): width = via.width if not height: height = via.height - self.add_layout_pin_rect_center(text=name, - layer=self.pwr_grid_layers[0], - offset=loc, - width=width, - height=height) + pin = self.add_layout_pin_rect_center(text=name, + layer=self.pwr_grid_layers[0], + offset=loc, + width=width, + height=height) + + return pin def copy_power_pin(self, pin, loc=None, directions=None, new_name=""): """ diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index d08e3f48..0432dec2 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -46,6 +46,8 @@ class bitcell_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 5bff2448..45285267 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -156,18 +156,15 @@ class bitcell_base_array(design.design): width=self.width, height=wl_pin.height()) - def add_supply_pins(self): - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) + def route_supplies(self): + for inst in self.cell_inst.values(): + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(inst, pin_name) def add_layout_pins(self): """ Add the layout pins """ self.add_bitline_pins() self.add_wl_pins() - self.add_supply_pins() def _adjust_x_offset(self, xoffset, col, col_offset): tempx = xoffset diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 01810323..df7a8241 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -280,7 +280,8 @@ class control_logic(design.design): def route_rails(self): """ Add the input signal inverted tracks """ height = self.control_logic_center.y - self.m2_pitch - offset = vector(self.ctrl_dff_array.width, 0) + # DFF spacing plus the power routing + offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0) self.input_bus = self.create_vertical_bus("m2", offset, @@ -312,7 +313,8 @@ class control_logic(design.design): self.place_dffs() # All of the control logic is placed to the right of the DFFs and bus - self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + # as well as the power supply stripe + self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch row = 0 # Add the logic on the right of the bus @@ -368,7 +370,7 @@ class control_logic(design.design): self.route_clk_buf() self.route_gated_clk_bar() self.route_gated_clk_buf() - self.route_supply() + self.route_supplies() def create_delay(self): """ Create the replica bitline """ @@ -707,28 +709,60 @@ class control_logic(design.design): start=out_pos, end=right_pos) - def route_supply(self): + def route_supplies(self): """ Add vdd and gnd to the instance cells """ - supply_layer = self.dff.get_pin("vdd").layer + pin_layer = self.dff.get_pin("vdd").layer + supply_layer = self.supply_stack[2] 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 = [] + for inst in self.row_end_inst: pins = inst.get_pins("vdd") for pin in pins: - if pin.layer == supply_layer: + if pin.layer == pin_layer: row_loc = pin.rc() pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("vdd", pin_loc, start_layer=pin.layer) - self.add_path(supply_layer, [row_loc, pin_loc]) + 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) + self.add_path(pin_layer, [row_loc, pin_loc]) pins = inst.get_pins("gnd") for pin in pins: - if pin.layer == supply_layer: + if pin.layer == pin_layer: row_loc = pin.rc() - pin_loc = vector(max_row_x_loc, pin.rc().y) - self.add_power_pin("gnd", pin_loc, start_layer=pin.layer) - self.add_path(supply_layer, [row_loc, pin_loc]) + 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) + self.add_path(pin_layer, [row_loc, pin_loc]) + + 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) + self.add_layout_pin_segment_center(text="vdd", + layer=supply_layer, + start=bot_pos, + end=top_pos) + + 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) + self.add_layout_pin_segment_center(text="gnd", + layer=supply_layer, + start=bot_pos, + end=top_pos) self.copy_layout_pin(self.delay_inst, "gnd") self.copy_layout_pin(self.delay_inst, "vdd") @@ -781,7 +815,7 @@ class control_logic(design.design): # Connect this at the bottom of the buffer out_pin = inst.get_pin("Z") out_pos = out_pin.center() - mid1 = vector(out_pos.x, out_pos.y - 0.4 * inst.mod.height) + mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height) mid2 = vector(self.input_bus[name].cx(), mid1.y) bus_pos = self.input_bus[name].center() self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos]) diff --git a/compiler/modules/delay_chain.py b/compiler/modules/delay_chain.py index a5bcea27..af446bbd 100644 --- a/compiler/modules/delay_chain.py +++ b/compiler/modules/delay_chain.py @@ -177,9 +177,11 @@ class delay_chain(design.design): # 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 OPTS.experimental_power: - self.route_horizontal_pins("vdd") - self.route_horizontal_pins("gnd") + 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] diff --git a/compiler/modules/dff_array.py b/compiler/modules/dff_array.py index 27f6f5bf..99b59064 100644 --- a/compiler/modules/dff_array.py +++ b/compiler/modules/dff_array.py @@ -42,6 +42,7 @@ class dff_array(design.design): self.height = self.rows * self.dff.height self.place_dff_array() + self.route_supplies() self.add_layout_pins() self.add_boundary() self.DRC_LVS() @@ -106,17 +107,25 @@ class dff_array(design.design): return dout_name + def route_supplies(self): + if OPTS.experimental_power and self.rows > 1: + # Vertical straps on ends if multiple rows + left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] + right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] + self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") + self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") + else: + for row in range(self.rows): + for col in range(self.columns): + # Continous vdd rail along with label. + vdd_pin=self.dff_insts[row, col].get_pin("vdd") + self.copy_power_pin(vdd_pin) + + # Continous gnd rail along with label. + gnd_pin=self.dff_insts[row, col].get_pin("gnd") + self.copy_power_pin(gnd_pin) + def add_layout_pins(self): - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin) - - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin) - for row in range(self.rows): for col in range(self.columns): din_pin = self.dff_insts[row, col].get_pin("D") diff --git a/compiler/modules/dff_buf_array.py b/compiler/modules/dff_buf_array.py index 6f8b54aa..62a7cfb6 100644 --- a/compiler/modules/dff_buf_array.py +++ b/compiler/modules/dff_buf_array.py @@ -145,24 +145,23 @@ class dff_buf_array(design.design): return dout_bar_name def route_supplies(self): - for row in range(self.rows): - vdd0_pin=self.dff_insts[row, 0].get_pin("vdd") - vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd") - self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height()) + if OPTS.experimental_power and self.rows > 1: + # Vertical straps on ends if multiple rows + left_dff_insts = [self.dff_insts[x, 0] for x in range(self.rows)] + right_dff_insts = [self.dff_insts[x, self.columns-1] for x in range(self.rows)] + self.route_vertical_pins("vdd", left_dff_insts, xside="lx", yside="cy") + self.route_vertical_pins("gnd", right_dff_insts, xside="rx", yside="cy") + else: + for row in range(self.rows): + for col in range(self.columns): + # Continous vdd rail along with label. + vdd_pin=self.dff_insts[row, col].get_pin("vdd") + self.copy_power_pin(vdd_pin) - gnd0_pin=self.dff_insts[row, 0].get_pin("gnd") - gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd") - self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height()) + # Continous gnd rail along with label. + gnd_pin=self.dff_insts[row, col].get_pin("gnd") + self.copy_power_pin(gnd_pin) - for row in range(self.rows): - for col in range(self.columns): - # Continous vdd rail along with label. - vdd_pin=self.dff_insts[row, col].get_pin("vdd") - self.copy_power_pin(vdd_pin, loc=vdd_pin.lc()) - - # Continous gnd rail along with label. - gnd_pin=self.dff_insts[row, col].get_pin("gnd") - self.copy_power_pin(gnd_pin, loc=gnd_pin.lc()) def add_layout_pins(self): diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 0786142f..83449a70 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -36,6 +36,8 @@ class dummy_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() @@ -98,6 +100,8 @@ class dummy_array(bitcell_base_array): width=self.width, height=wl_pin.height()) + def route_supplies(self): + # Copy a vdd/gnd layout pin from every cell for row in range(self.row_size): for col in range(self.column_size): diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index f9987bb8..2b173969 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -28,7 +28,6 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] self.pre4x16_inst = [] - self.local_insts = [] b = factory.create(module_type=OPTS.bitcell) self.cell_height = b.height @@ -591,65 +590,20 @@ class hierarchical_decoder(design.design): def route_supplies(self): """ - Add a pin for each row of vdd/gnd which are - must-connects next level up. """ - # This is an experiment with power rails - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - 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, 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") - else: - 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, xside="rx") - self.route_vertical_pins("gnd", insts=self.and_inst, xside="lx") + # 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") - # Widen the rails to cover any gap - for inst in self.and_inst: - for name in ["vdd", "gnd"]: - supply_pin = inst.get_pin(name) - self.add_segment_center(layer=supply_pin.layer, - start=vector(0, supply_pin.cy()), - end=vector(self.width, supply_pin.cy())) - else: - if layer_props.hierarchical_decoder.vertical_supply: - for n in ["vdd", "gnd"]: - pins = self.and_inst[0].get_pins(n) - for pin in pins: - self.add_rect(layer=pin.layer, - offset=pin.ll() + vector(0, self.bus_space), - width=pin.width(), - height=self.height - 2 * self.bus_space) + 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()) - # This adds power vias at the top of each cell - # (except the last to keep them inside the boundary) - for i in self.and_inst[:-1]: - pins = i.get_pins(n) - for pin in pins: - self.copy_power_pin(pin, loc=pin.uc()) - - for i in self.pre2x4_inst + self.pre3x8_inst: - self.copy_layout_pin(i, n) - else: - # The vias will be placed at the right of the cells. - xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space - for row in range(0, self.num_outputs): - for pin_name in ["vdd", "gnd"]: - # The nand and inv are the same height rows... - supply_pin = self.and_inst[row].get_pin(pin_name) - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) - - # Copy the pins from the predecoders - for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst: - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(pre, pin_name) 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 706fc58e..0d7f31a3 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -381,7 +381,7 @@ class hierarchical_predecode(design.design): def route_supplies(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # We may ahve vertical power supply rails + # We may have vertical power supply rails if layer_props.hierarchical_predecode.vertical_supply and not self.column_decoder: for n in ["vdd", "gnd"]: # This makes a wire from top to bottom for both inv and and gates @@ -403,8 +403,6 @@ class hierarchical_predecode(design.design): # In other techs, we are using standard cell decoder cells with horizontal power else: for num in range(0, self.number_of_outputs): - - # Route both supplies for n in ["vdd", "gnd"]: and_pins = self.and_inst[num].get_pins(n) for and_pin in and_pins: @@ -418,4 +416,4 @@ class hierarchical_predecode(design.design): else: xoffset = self.inv_inst[0].lx() - self.bus_space pin_pos = vector(xoffset, and_pin.cy()) - self.copy_power_pin(and_pin, loc=pin_pos) + self.add_power_pin(n, pin_pos) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 7c3af51c..2757a86f 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -76,15 +76,21 @@ class port_address(design.design): def route_supplies(self): """ Propagate all vdd/gnd pins up to this level for all modules """ - for inst in [self.wordline_driver_array_inst, self.row_decoder_inst]: - self.copy_layout_pin(inst, "vdd") - self.copy_layout_pin(inst, "gnd") - - for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"): - if layer_props.port_address.supply_offset: - self.copy_power_pin(rbl_vdd_pin) - else: - self.copy_power_pin(rbl_vdd_pin, loc=rbl_vdd_pin.lc()) + 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) # Also connect the B input of the RBL and_dec to vdd if OPTS.local_array_size == 0: diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index cfd2015b..c820dfc1 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) + self.route_horizontal_pins("en_bar", layer=self.en_bar_layer, num_pins=1) for inst in self.local_insts: self.add_via_stack_center(from_layer=en_pin.layer, to_layer=self.en_bar_layer, @@ -95,11 +95,7 @@ class precharge_array(design.design): self.copy_layout_pin(inst, "br", "br_{0}".format(i)) def route_supplies(self): - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - self.route_horizontal_pins("vdd") - else: - for inst in self.local_insts: - self.copy_layout_pin(inst, "vdd") + self.route_horizontal_pins("vdd") def create_insts(self): """Creates a precharge array by horizontally tiling the precharge cell""" diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index be6d1ef5..5a7a046f 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -305,19 +305,18 @@ class replica_bitcell_array(bitcell_base_array): def create_layout(self): - # We will need unused wordlines grounded, so we need to know their layer - # and create a space on the left and right for the vias to connect to ground - pin = self.cell.get_pin(self.cell.get_all_wl_names()[0]) - pin_layer = pin.layer - self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) - self.unused_offset = vector(self.unused_pitch, 0) + # This creates space for the unused wordline connections as well as the + # row-based or column based power and ground lines. + self.vertical_pitch = getattr(self, "{}_pitch".format(self.supply_stack[0])) + self.horizontal_pitch = getattr(self, "{}_pitch".format(self.supply_stack[2])) + self.unused_offset = vector(2 * self.horizontal_pitch, 2 * self.vertical_pitch) # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) self.col_end_offset = vector(self.cell.width, self.cell.height) self.row_end_offset = vector(self.cell.width, self.cell.height) - # Everything is computed with the main array at (self.unused_pitch, 0) to start + # Everything is computed with the main array self.bitcell_array_inst.place(offset=self.unused_offset) self.add_replica_columns() @@ -336,6 +335,8 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.route_unused_wordlines() self.add_boundary() @@ -458,23 +459,82 @@ class replica_bitcell_array(bitcell_base_array): width=pin.width(), height=self.height) - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - self.route_vertical_pins(name="gnd", insts=self.replica_col_insts) - self.route_horizontal_pins(name="vdd", insts=self.dummy_row_insts) - else: - # vdd/gnd are only connected in the perimeter cells - # replica column should only have a vdd/gnd in the dummy cell on top/bottom - supply_insts = self.dummy_col_insts + self.dummy_row_insts + def route_supplies(self): - for pin_name in self.supplies: - for inst in supply_insts: - pin_list = inst.get_pins(pin_name) - for pin in pin_list: - self.copy_power_pin(pin) + # vdd/gnd are only connected in the perimeter cells + # replica column should only have a vdd/gnd in the dummy cell on top/bottom + supply_insts = self.dummy_col_insts + self.dummy_row_insts + + vdd_leftx = 0 + vdd_rightx = 0 + gnd_leftx = 0 + gnd_rightx = 0 + + for inst in supply_insts: + for pin in inst.get_pins("vdd"): + (vdd_leftx, vdd_rightx) = self.connect_pin(pin, 2.5) + for pin in inst.get_pins("gnd"): + (gnd_leftx, gnd_rightx) = self.connect_pin(pin) + + + self.add_layout_end_pin_segment_center(text="vdd", + layer=self.supply_stack[2], + start=vector(vdd_leftx, 0), + end=vector(vdd_leftx, self.height)) + self.add_layout_end_pin_segment_center(text="vdd", + layer=self.supply_stack[2], + start=vector(vdd_rightx, 0), + end=vector(vdd_rightx, self.height)) + self.add_layout_end_pin_segment_center(text="gnd", + layer=self.supply_stack[2], + start=vector(gnd_leftx, 0), + end=vector(gnd_leftx, self.height)) + self.add_layout_end_pin_segment_center(text="gnd", + layer=self.supply_stack[2], + start=vector(gnd_rightx, 0), + end=vector(gnd_rightx, self.height)) + + + def route_unused_wordlines(self): + """ Connect the unused RBL and dummy wordlines to gnd """ + # This grounds all the dummy row word lines + for inst in self.dummy_row_insts: + for wl_name in self.col_cap_top.get_wordline_names(): + pin = inst.get_pin(wl_name) + self.connect_pin(pin) + + # Ground the unused replica wordlines + for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): + for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): + if wl_name in self.gnd_wordline_names: + pin = inst.get_pin(pin_name) + self.connect_pin(pin) + + + def connect_pin(self, pin, offset_multiple=1): + + pin_layer = pin.layer + + left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) + right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) + + # Place the pins a track outside of the array + left_loc = left_pin_loc - vector(offset_multiple * self.horizontal_pitch, 0) + right_loc = right_pin_loc + vector(offset_multiple * self.horizontal_pitch, 0) + self.add_via_stack_center(offset=left_loc, + from_layer=pin_layer, + to_layer=self.supply_stack[2], + directions=("H", "H")) + self.add_via_stack_center(offset=right_loc, + from_layer=pin_layer, + to_layer=self.supply_stack[2], + directions=("H", "H")) + + # Add a path to connect to the array + self.add_path(pin_layer, [left_loc, right_loc]) + + return (left_loc.x, right_loc.x) - for inst in self.replica_col_insts: - if inst: - self.copy_layout_pin(inst, pin_name) def analytical_power(self, corner, load): @@ -494,34 +554,6 @@ class replica_bitcell_array(bitcell_base_array): cell_power.leakage * self.column_size * self.row_size) return total_power - def route_unused_wordlines(self): - """ Connect the unused RBL and dummy wordlines to gnd """ - # This grounds all the dummy row word lines - for inst in self.dummy_row_insts: - for wl_name in self.col_cap_top.get_wordline_names(): - self.ground_pin(inst, wl_name) - - # Ground the unused replica wordlines - for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): - for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): - if wl_name in self.gnd_wordline_names: - self.ground_pin(inst, pin_name) - - def ground_pin(self, inst, name): - pin = inst.get_pin(name) - pin_layer = pin.layer - - left_pin_loc = vector(self.dummy_col_insts[0].lx(), pin.cy()) - right_pin_loc = vector(self.dummy_col_insts[1].rx(), pin.cy()) - - # Place the pins a track outside of the array - left_loc = left_pin_loc - vector(self.unused_pitch, 0) - right_loc = right_pin_loc + vector(self.unused_pitch, 0) - self.add_power_pin("gnd", left_loc, directions=("H", "H")) - self.add_power_pin("gnd", right_loc, directions=("H", "H")) - - # Add a path to connect to the array - self.add_path(pin_layer, [left_loc, right_loc], width=pin.height()) def gen_bl_wire(self): if OPTS.netlist_only: diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index a3de3a38..43a3cda3 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -69,6 +69,8 @@ class replica_column(bitcell_base_array): self.add_layout_pins() + self.route_supplies() + self.add_boundary() self.DRC_LVS() @@ -185,15 +187,11 @@ class replica_column(bitcell_base_array): width=self.width, height=wl_pin.height()) - # Supplies are only connected in the ends - for (index, inst) in enumerate(self.cell_inst): + def route_supplies(self): + + for inst in self.cell_inst: for pin_name in ["vdd", "gnd"]: - if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: - #for pin in inst.get_pins(pin_name): - # self.copy_power_pin(pin) - self.copy_power_pins(inst, pin_name) - else: - self.copy_layout_pin(inst, pin_name) + self.copy_layout_pin(inst, pin_name) def get_bitline_names(self, port=None): if port == None: diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index b4c2c54f..276194ee 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -53,7 +53,7 @@ class wordline_driver_array(design.design): self.height = self.wld_inst[-1].uy() self.add_boundary() - self.route_vdd_gnd() + self.route_supplies() self.DRC_LVS() def add_pins(self): @@ -72,54 +72,16 @@ class wordline_driver_array(design.design): self.wl_driver = factory.create(module_type="wordline_driver", cols=self.cols) - def route_vdd_gnd(self): + def route_supplies(self): """ Add vertical power rails. """ - # Experiment with power straps - if OPTS.tech_name=="sky130" or OPTS.experimental_power: - if layer_props.wordline_driver.vertical_supply: - 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, 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): - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - self.add_segment_center(layer=supply_pin.layer, - start=vector(0, supply_pin.cy()), - end=vector(self.width, supply_pin.cy())) - else: - if layer_props.wordline_driver.vertical_supply: - for name in ["vdd", "gnd"]: - supply_pins = self.wld_inst[0].get_pins(name) - for pin in supply_pins: - self.add_layout_pin_segment_center(text=name, - layer=pin.layer, - start=pin.bc(), - end=vector(pin.cx(), self.height)) - else: - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.wld_inst[0].rx()] - for num in range(self.rows): - # this will result in duplicate polygons for rails, but who cares - - # use the inverter offset even though it will be the and's too - (gate_offset, y_dir) = self.get_gate_offset(0, - self.wl_driver.height, - num) - # Route both supplies - for name in ["vdd", "gnd"]: - supply_pin = self.wld_inst[num].get_pin(name) - - # Add pins in two locations - for xoffset in xoffset_list: - pin_pos = vector(xoffset, supply_pin.cy()) - self.copy_power_pin(supply_pin, loc=pin_pos) + 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") def create_drivers(self): diff --git a/compiler/options.py b/compiler/options.py index ebf716b0..c7c062ba 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -195,4 +195,4 @@ class options(optparse.Values): write_mask_and_array = "write_mask_and_array" # Non-public options - experimental_power = False + experimental_power = True diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index 110b8330..a410e04a 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -349,8 +349,6 @@ class sram_1bank(sram_base): if OPTS.perimeter_pins: # We now route the escape routes far enough out so that they will # reach past the power ring or stripes on the sides - # The power rings are 4 tracks wide with 2 tracks spacing, so space it - # 11 tracks out bbox = self.get_bbox(side="ring", margin=11*rt.track_width) self.route_escape_pins(bbox)