Power routing changes.

Make the power rails an "experimental_power" option and conditional.
Rename route_vdd_gnd to route_supplies everywhere for consistency.
This commit is contained in:
mrg 2022-03-06 09:56:00 -08:00
parent 8b3c10ae79
commit d69e55c2e3
11 changed files with 227 additions and 100 deletions

View File

@ -478,17 +478,17 @@ class layout():
top_pos = vector(x, top_y) top_pos = vector(x, top_y)
bot_pos = vector(x, bot_y) bot_pos = vector(x, bot_y)
self.add_segment_center(layer=pin_layer, rect = self.add_layout_pin_rect_center(text=name,
start=bot_pos, layer=pin_layer,
end=top_pos, offset=top_pos)
width=via_width)
self.add_layout_pin_rect_center(text=name,
layer=pin_layer,
offset=top_pos)
# self.add_layout_pin_rect_center(text=name, # self.add_layout_pin_rect_center(text=name,
# layer=pin_layer, # layer=pin_layer,
# offset=bot_pos) # 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"): 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) left_pos = vector(left_x, y)
right_pos = vector(right_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, rect = self.add_layout_pin_rect_center(text=name,
layer=pin_layer, layer=pin_layer,
offset=left_pos) offset=left_pos)
# self.add_layout_pin_rect_center(text=name, # self.add_layout_pin_rect_center(text=name,
# layer=pin_layer, # layer=pin_layer,
# offset=right_pos) # 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): def add_layout_pin_segment_center(self, text, layer, start, end, width=None):
""" """

View File

@ -63,7 +63,7 @@ class hierarchical_decoder(design.design):
self.width = self.and_inst[0].rx() self.width = self.and_inst[0].rx()
self.route_vdd_gnd() self.route_supplies()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -589,50 +589,67 @@ class hierarchical_decoder(design.design):
output_index) output_index)
output_index = output_index + 1 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 Add a pin for each row of vdd/gnd which are
must-connects next level up. 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 # This is an experiment with power rails
# (except the last to keep them inside the boundary) if OPTS.experimental_power:
for i in self.and_inst[:-1]: if layer_props.hierarchical_decoder.vertical_supply:
pins = i.get_pins(n) pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst
for pin in pins: self.route_vertical_pins("vdd", insts=pre_insts, yside="by")
self.copy_power_pin(pin, loc=pin.uc()) 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: # Widen the rails to cover any gap
self.copy_layout_pin(i, n) 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: else:
pre_insts = self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst if layer_props.hierarchical_decoder.vertical_supply:
self.route_vertical_pins("vdd", insts=pre_insts) for n in ["vdd", "gnd"]:
self.route_vertical_pins("gnd", insts=pre_insts) pins = self.and_inst[0].get_pins(n)
self.route_vertical_pins("vdd", insts=self.and_inst, xside="rx") for pin in pins:
self.route_vertical_pins("gnd", insts=self.and_inst, xside="lx") 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 # This adds power vias at the top of each cell
for inst in self.and_inst: # (except the last to keep them inside the boundary)
for name in ["vdd", "gnd"]: for i in self.and_inst[:-1]:
supply_pin = inst.get_pin(name) pins = i.get_pins(n)
self.add_segment_center(layer=supply_pin.layer, for pin in pins:
start=vector(0, supply_pin.cy()), self.copy_power_pin(pin, loc=pin.uc())
end=vector(self.width, supply_pin.cy()))
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): def route_predecode_bus_outputs(self, rail_name, pin, row):
""" """

View File

@ -189,7 +189,7 @@ class hierarchical_predecode(design.design):
self.route_output_inverters() self.route_output_inverters()
self.route_inputs_to_rails() self.route_inputs_to_rails()
self.route_output_ands() self.route_output_ands()
self.route_vdd_gnd() self.route_supplies()
def route_inputs_to_rails(self): def route_inputs_to_rails(self):
""" Route the uninverted inputs to the second set of rails """ """ Route the uninverted inputs to the second set of rails """
@ -378,7 +378,7 @@ class hierarchical_predecode(design.design):
offset=pin_pos, offset=pin_pos,
directions=direction) 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. """ """ 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 ahve vertical power supply rails

View File

@ -230,7 +230,7 @@ class port_address(design.design):
# This m4_pitch corresponds to the offset space for jog routing in the # This m4_pitch corresponds to the offset space for jog routing in the
# wordline_driver_array # 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: if self.port == 0:
self.rbl_driver_inst.place(rbl_driver_offset, "MX") self.rbl_driver_inst.place(rbl_driver_offset, "MX")

View File

@ -68,6 +68,7 @@ class precharge_array(design.design):
self.height = self.pc_cell.height self.height = self.pc_cell.height
self.add_layout_pins() self.add_layout_pins()
self.route_supplies()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -81,14 +82,24 @@ class precharge_array(design.design):
def add_layout_pins(self): 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)
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)): for i in range(len(self.local_insts)):
inst = self.local_insts[i] inst = self.local_insts[i]
self.copy_layout_pin(inst, "bl", "bl_{0}".format(i)) self.copy_layout_pin(inst, "bl", "bl_{0}".format(i))
self.copy_layout_pin(inst, "br", "br_{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): def create_insts(self):
"""Creates a precharge array by horizontally tiling the precharge cell""" """Creates a precharge array by horizontally tiling the precharge cell"""

View File

@ -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 # 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 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) if OPTS.experimental_power:
#self.route_horizontal_pins(name=pin_name, insts=self.replica_col_insts) for pin_name in self.supplies:
for inst in supply_insts: #self.route_vertical_pins(name=pin_name, insts=supply_insts)
pin_list = inst.get_pins(pin_name) self.route_horizontal_pins(name=pin_name, insts=supply_insts)
for pin in pin_list:
self.copy_power_pin(pin) #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): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""

View File

@ -75,8 +75,8 @@ class sense_amp_array(design.design):
self.width = self.local_insts[-1].rx() self.width = self.local_insts[-1].rx()
self.add_layout_pins() self.add_layout_pins()
self.route_horizontal_pins("vdd")
self.route_horizontal_pins("gnd") self.route_supplies()
self.route_rails() self.route_rails()
self.add_boundary() self.add_boundary()
@ -173,6 +173,20 @@ class sense_amp_array(design.design):
width=dout_pin.width(), width=dout_pin.width(),
height=dout_pin.height()) 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): def route_rails(self):
# Add enable across the array # Add enable across the array
en_pin = self.amp.get_pin(self.amp.en_name) en_pin = self.amp.get_pin(self.amp.en_name)

View File

@ -45,7 +45,7 @@ class wordline_driver_array(design.design):
self.place_drivers() self.place_drivers()
self.route_layout() 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 # Leave a well gap to separate the bitcell array well from this well
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") 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. 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 # Experiment with power straps
for num in range(self.rows): 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"]: for name in ["vdd", "gnd"]:
supply_pin = self.wld_inst[num].get_pin(name) supply_pins = self.wld_inst[0].get_pins(name)
self.add_segment_center(layer=supply_pin.layer, for pin in supply_pins:
start=vector(0, supply_pin.cy()), self.add_layout_pin_segment_center(text=name,
end=vector(self.width, supply_pin.cy())) 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): def create_drivers(self):

View File

@ -72,6 +72,7 @@ class write_driver_array(design.design):
self.width = self.local_insts[-1].rx() self.width = self.local_insts[-1].rx()
self.height = self.driver.height self.height = self.driver.height
self.add_layout_pins() self.add_layout_pins()
self.route_supplies()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -204,9 +205,6 @@ class write_driver_array(design.design):
width=br_pin.width(), width=br_pin.width(),
height=br_pin.height()) height=br_pin.height())
self.route_horizontal_pins("vdd")
self.route_horizontal_pins("gnd")
if self.write_size: if self.write_size:
for bit in range(self.num_wmasks): for bit in range(self.num_wmasks):
inst = self.local_insts[bit * self.write_size] inst = self.local_insts[bit * self.write_size]
@ -256,3 +254,17 @@ class write_driver_array(design.design):
layer="m1", layer="m1",
offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1),
width=self.width) 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"))

View File

@ -197,3 +197,6 @@ class options(optparse.Values):
write_driver_array = "write_driver_array" write_driver_array = "write_driver_array"
write_driver = "write_driver" write_driver = "write_driver"
write_mask_and_array = "write_mask_and_array" write_mask_and_array = "write_mask_and_array"
# Non-public options
experimental_power = False

View File

@ -71,7 +71,7 @@ class precharge(design.design):
self.connect_poly() self.connect_poly()
self.route_en() self.route_en()
self.place_nwell_and_contact() self.place_nwell_and_contact()
self.route_vdd() self.route_supplies()
self.route_bitlines() self.route_bitlines()
self.connect_to_bitlines() self.connect_to_bitlines()
self.add_boundary() self.add_boundary()
@ -91,28 +91,51 @@ class precharge(design.design):
mults=self.ptx_mults, mults=self.ptx_mults,
tx_type="pmos") tx_type="pmos")
def route_vdd(self): def route_supplies(self):
""" """
Adds a vdd rail at the top of the cell Adds a vdd rail at the top of the cell
""" """
pmos_pin = self.upper_pmos2_inst.get_pin("S") if OPTS.experimental_power:
pmos_pos = pmos_pin.center() pmos_pin = self.upper_pmos2_inst.get_pin("S")
self.add_path(pmos_pin.layer, [pmos_pos, self.well_contact_pos]) 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, self.add_via_stack_center(from_layer=pmos_pin.layer,
to_layer=self.supply_stack[0], to_layer=self.supply_stack[0],
offset=self.well_contact_pos,
directions=("V", "V"))
self.add_min_area_rect_center(layer=self.en_layer,
offset=self.well_contact_pos, offset=self.well_contact_pos,
width=self.well_contact.mod.second_layer_width) directions=("V", "V"))
self.add_layout_pin_rect_center(text="vdd", self.add_min_area_rect_center(layer=self.en_layer,
layer=self.supply_stack[0], offset=self.well_contact_pos,
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): def create_ptx(self):
""" """