diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 93d2de51..b3eff27d 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -478,17 +478,17 @@ class layout(): 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) - - self.add_layout_pin_rect_center(text=name, - layer=pin_layer, - offset=top_pos) + rect = 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) + self.add_segment_center(layer=pin_layer, + start=vector(rect.cx(), bot_pos.y), + end=rect.bc(), + width=via_width) + def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy"): @@ -543,17 +543,21 @@ class layout(): 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) + rect = 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) + # 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=rect.rc(), + end=vector(right_pos.x, rect.cy()), + width=via_height) + 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 e9f8bbd8..0db5acbb 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -63,7 +63,7 @@ class hierarchical_decoder(design.design): self.width = self.and_inst[0].rx() - self.route_vdd_gnd() + self.route_supplies() self.add_boundary() self.DRC_LVS() @@ -589,50 +589,67 @@ class hierarchical_decoder(design.design): output_index) output_index = output_index + 1 - def route_vdd_gnd(self): + def route_supplies(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - 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") - return - 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) - # 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()) + # This is an experiment with power rails + if 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") - for i in self.pre2x4_inst + self.pre3x8_inst: - self.copy_layout_pin(i, n) + # 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: - 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") + 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) - # 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())) + # 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 f3dd8e7e..706fc58e 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -189,7 +189,7 @@ class hierarchical_predecode(design.design): self.route_output_inverters() self.route_inputs_to_rails() self.route_output_ands() - self.route_vdd_gnd() + self.route_supplies() def route_inputs_to_rails(self): """ Route the uninverted inputs to the second set of rails """ @@ -378,7 +378,7 @@ class hierarchical_predecode(design.design): offset=pin_pos, directions=direction) - def route_vdd_gnd(self): + 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 diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 5e0b74fa..7c3af51c 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -230,7 +230,7 @@ class port_address(design.design): # 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) + rbl_driver_offset = wordline_driver_array_offset + vector(2 * self.m4_pitch, 0) if self.port == 0: self.rbl_driver_inst.place(rbl_driver_offset, "MX") diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index d2864ff4..0215d76a 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -68,6 +68,7 @@ class precharge_array(design.design): self.height = self.pc_cell.height self.add_layout_pins() + self.route_supplies() self.add_boundary() self.DRC_LVS() @@ -81,14 +82,24 @@ 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("vdd") + for inst in self.local_insts: + self.add_via_stack_center(from_layer=en_pin.layer, + to_layer=self.en_bar_layer, + offset=inst.get_pin("en_bar").center()) for i in range(len(self.local_insts)): inst = self.local_insts[i] self.copy_layout_pin(inst, "bl", "bl_{0}".format(i)) self.copy_layout_pin(inst, "br", "br_{0}".format(i)) + def route_supplies(self): + if OPTS.experimental_power: + self.route_horizontal_pins("vdd") + else: + for inst in self.local_insts: + self.copy_layout_pin(inst, "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 88701161..57a58d79 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -462,20 +462,33 @@ class replica_bitcell_array(bitcell_base_array): # 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 - 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: - self.copy_power_pin(pin) + if OPTS.experimental_power: + 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: + self.copy_power_pin(pin) + + for inst in self.replica_col_insts: + if inst: + self.copy_layout_pin(inst, pin_name) + else: + 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) + + for inst in self.replica_col_insts: + if inst: + self.copy_layout_pin(inst, pin_name) - for inst in self.replica_col_insts: - if inst: - self.copy_layout_pin(inst, pin_name) def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 025328ec..a481679f 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -75,8 +75,8 @@ class sense_amp_array(design.design): self.width = self.local_insts[-1].rx() self.add_layout_pins() - self.route_horizontal_pins("vdd") - self.route_horizontal_pins("gnd") + + self.route_supplies() self.route_rails() self.add_boundary() @@ -173,6 +173,20 @@ class sense_amp_array(design.design): width=dout_pin.width(), height=dout_pin.height()) + def route_supplies(self): + if OPTS.experimental_power: + self.route_horizontal_pins("vdd") + self.route_horizontal_pins("gnd") + else: + for i in range(len(self.local_insts)): + inst = self.local_insts[i] + + for gnd_pin in inst.get_pins("gnd"): + self.copy_power_pin(gnd_pin) + + for vdd_pin in inst.get_pins("vdd"): + self.copy_power_pin(vdd_pin) + def route_rails(self): # Add enable across the array en_pin = self.amp.get_pin(self.amp.en_name) diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index 31b60887..a13f4bcd 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -45,7 +45,7 @@ class wordline_driver_array(design.design): self.place_drivers() self.route_layout() - self.offset_x_coordinates(vector(-self.m4_pitch, 0)) + self.offset_x_coordinates(vector(-2*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") @@ -76,20 +76,50 @@ class wordline_driver_array(design.design): """ Add vertical power rails. """ - 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): + # Experiment with power straps + if 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_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())) + 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) def create_drivers(self): diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index e4192f4a..f232c129 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -72,6 +72,7 @@ class write_driver_array(design.design): self.width = self.local_insts[-1].rx() self.height = self.driver.height self.add_layout_pins() + self.route_supplies() self.add_boundary() self.DRC_LVS() @@ -204,9 +205,6 @@ class write_driver_array(design.design): width=br_pin.width(), height=br_pin.height()) - self.route_horizontal_pins("vdd") - self.route_horizontal_pins("gnd") - if self.write_size: for bit in range(self.num_wmasks): inst = self.local_insts[bit * self.write_size] @@ -256,3 +254,17 @@ class write_driver_array(design.design): layer="m1", offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), width=self.width) + + def route_supplies(self): + if OPTS.experimental_power: + self.route_horizontal_pins("vdd") + self.route_horizontal_pins("gnd") + else: + for i in range(self.word_size + self.num_spare_cols): + inst = self.local_insts[i] + for n in ["vdd", "gnd"]: + pin_list = inst.get_pins(n) + for pin in pin_list: + self.copy_power_pin(pin, directions=("V", "V")) + + diff --git a/compiler/options.py b/compiler/options.py index f3c19283..d81a8c7e 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -197,3 +197,6 @@ class options(optparse.Values): write_driver_array = "write_driver_array" write_driver = "write_driver" write_mask_and_array = "write_mask_and_array" + + # Non-public options + experimental_power = False diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index aa1f1a70..8eff8c8f 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -71,7 +71,7 @@ class precharge(design.design): self.connect_poly() self.route_en() self.place_nwell_and_contact() - self.route_vdd() + self.route_supplies() self.route_bitlines() self.connect_to_bitlines() self.add_boundary() @@ -91,28 +91,51 @@ class precharge(design.design): mults=self.ptx_mults, tx_type="pmos") - def route_vdd(self): + def route_supplies(self): """ Adds a vdd rail at the top of the cell """ - pmos_pin = self.upper_pmos2_inst.get_pin("S") - pmos_pos = pmos_pin.center() - self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) + if OPTS.experimental_power: + pmos_pin = self.upper_pmos2_inst.get_pin("S") + pmos_pos = pmos_pin.center() + self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) - self.add_via_stack_center(from_layer=pmos_pin.layer, - to_layer=self.supply_stack[0], - offset=self.well_contact_pos, - directions=("V", "V")) - - self.add_min_area_rect_center(layer=self.en_layer, + self.add_via_stack_center(from_layer=pmos_pin.layer, + to_layer=self.supply_stack[0], offset=self.well_contact_pos, - width=self.well_contact.mod.second_layer_width) + directions=("V", "V")) - self.add_layout_pin_rect_center(text="vdd", - layer=self.supply_stack[0], - offset=self.well_contact_pos) + self.add_min_area_rect_center(layer=self.en_layer, + offset=self.well_contact_pos, + width=self.well_contact.mod.second_layer_width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.supply_stack[0], + offset=self.well_contact_pos) + else: + # Adds the rail across the width of the cell + vdd_position = vector(0.5 * self.width, self.height) + layer_width = drc("minwidth_" + self.en_layer) + self.add_rect_center(layer=self.en_layer, + offset=vdd_position, + width=self.width, + height=layer_width) + + pmos_pin = self.upper_pmos2_inst.get_pin("S") + + # center of vdd rail + pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) + self.add_path(self.en_layer, [pmos_pin.center(), pmos_vdd_pos]) + + self.add_power_pin("vdd", + self.well_contact_pos, + directions=("V", "V")) + + self.add_via_stack_center(from_layer=pmos_pin.layer, + to_layer=self.en_layer, + offset=pmos_pin.center(), + directions=("V", "V")) def create_ptx(self): """