Merge branch 'singleport_refactor' into array_gen

This commit is contained in:
Jesse Cirimelli-Low
2025-02-24 23:26:28 -08:00
35 changed files with 1133 additions and 2828 deletions
+113 -96
View File
@@ -51,10 +51,10 @@ class capped_replica_bitcell_array(bitcell_base_array):
self.rbls = self.left_rbl + self.right_rbl
# Two dummy rows plus replica even if we don't add the column
self.extra_rows = sum(self.rbl)
self.extra_rows = sum(self.rbl) + 2
# If we aren't using row/col caps, then we need to use the bitcell
if not self.cell.end_caps:
self.extra_rows += 2
#if not self.cell.end_caps:
# self.extra_rows += 2
self.create_netlist()
if not OPTS.netlist_only:
@@ -85,7 +85,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=self.column_size + len(self.rbls),
rows=1,
# dummy column + left replica column(s)
column_offset=1,
column_offset=0,
mirror=0,
location="top")
@@ -93,7 +93,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=self.column_size + len(self.rbls),
rows=1,
# dummy column + left replica column(s)
column_offset=1,
column_offset=0,
mirror=0,
location="bottom")
@@ -104,7 +104,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
cols=1,
column_offset=0,
rows=self.row_size + self.extra_rows,
mirror=(self.rbl[0] + 1) % 2)
location="left")
self.row_cap_right = factory.create(module_type=row_cap_module_type,
cols=1,
@@ -112,9 +112,9 @@ class capped_replica_bitcell_array(bitcell_base_array):
# + left replica column(s)
# + bitcell columns
# + right replica column(s)
column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0],
column_offset=len(self.left_rbl) + self.column_size + self.rbl[0],
rows=self.row_size + self.extra_rows,
mirror=(self.rbl[0] + 1) % 2)
location="right")
def add_pins(self):
@@ -201,40 +201,34 @@ class capped_replica_bitcell_array(bitcell_base_array):
# row-based or column based power and ground lines.
self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0]))
self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2]))
# FIXME: custom sky130 replica module has a better version of this offset
self.unused_offset = vector(0.25, 0.25)
# 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 replica array
self.replica_bitcell_array_inst.place(offset=self.unused_offset)
self.replica_bitcell_array_inst.place(offset=0)
self.add_end_caps()
# shift everything up and right to account for cap cells
self.translate_all(self.bitcell_offset.scale(-1, -1))
self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x
self.height = self.dummy_row_insts[1].uy()
self.add_layout_pins()
ll = vector(-1 * self.dummy_col_insts[0].width, -1 * self.dummy_row_insts[0].height)
self.translate_all(ll)
self.capped_rba_width = self.dummy_col_insts[0].width + self.dummy_row_insts[0].width + self.dummy_col_insts[1].width
self.capped_rba_height = self.dummy_col_insts[0].height
#self.route_power_ring(self.supply_stack[2], self.supply_stack[0])
self.route_supplies()
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.reset_coordinates()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def route_power_ring(self, v_layer, h_layer):
self.bbox = (vector(0,0), vector(self.capped_rba_width, self.capped_rba_height))
self.supply_rail_width = drc["minwidth_m3"]
self.supply_rail_pitch = 6 * self.supply_rail_width
self.add_power_ring(v_layer=v_layer, h_layer=h_layer)
def get_main_array_top(self):
return self.replica_bitcell_array_inst.by() + self.replica_bitcell_array.get_main_array_top()
@@ -248,7 +242,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
def get_main_array_right(self):
return self.replica_bitcell_array_inst.lx() + self.replica_bitcell_array.get_main_array_right()
# FIXME: these names need to be changed to reflect what they're actually returning
#FIXME: these names need to be changed to reflect what they're actually returning
def get_replica_top(self):
return self.dummy_row_insts[1].by()
@@ -261,7 +255,6 @@ class capped_replica_bitcell_array(bitcell_base_array):
def get_replica_right(self):
return self.dummy_col_insts[1].rx()
def get_column_offsets(self):
"""
Return an array of the x offsets of all the regular bits
@@ -273,26 +266,23 @@ class capped_replica_bitcell_array(bitcell_base_array):
def add_end_caps(self):
""" Add dummy cells or end caps around the array """
# Far top dummy row (first row above array is NOT flipped if even number of rows)
flip_dummy = (self.row_size + self.rbl[1]) % 2
dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy) + self.replica_bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far top dummy row
offset = self.replica_bitcell_array_inst.ul()
self.dummy_row_insts[1].place(offset=offset)
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.rbl[0] + 1) % 2
dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy - 1) + self.unused_offset
self.dummy_row_insts[0].place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# Far bottom dummy row
dummy_row_height = vector(0, self.dummy_row_insts[0].height)
offset = self.replica_bitcell_array_inst.ll() - dummy_row_height
self.dummy_row_insts[0].place(offset=offset)
# Far left dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(-1, -1) + self.unused_offset
self.dummy_col_insts[0].place(offset=dummy_col_offset)
dummy_col_width = vector(self.dummy_col_insts[0].width, 0)
offset = self.dummy_row_insts[0].ll() - dummy_col_width
self.dummy_col_insts[0].place(offset=offset)
# Far right dummy col
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
dummy_col_offset = self.bitcell_offset.scale(0, -1) + self.replica_bitcell_array_inst.lr()
self.dummy_col_insts[1].place(offset=dummy_col_offset)
offset = self.dummy_row_insts[0].lr()
self.dummy_col_insts[1].place(offset=offset)
def add_layout_pins(self):
for pin_name in self.used_wordline_names + self.bitline_pin_list:
@@ -301,13 +291,13 @@ class capped_replica_bitcell_array(bitcell_base_array):
if "wl" in pin_name:
# wordlines
pin_offset = pin.ll().scale(0, 1)
pin_width = self.width
pin_width = self.capped_rba_width
pin_height = pin.height()
else:
# bitlines
pin_offset = pin.ll().scale(1, 0)
pin_width = pin.width()
pin_height = self.height
pin_height = self.capped_rba_height
self.add_layout_pin(text=pin_name,
layer=pin.layer,
@@ -321,56 +311,83 @@ class capped_replica_bitcell_array(bitcell_base_array):
bitcell = factory.create(module_type="pbitcell")
else:
bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports))
top = True
bottom = True
left = False
right = False
if top:
inst = self.dummy_row_insts[1]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.top_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
vdd_dir = bitcell.vdd_dir
gnd_dir = bitcell.gnd_dir
array_pins = inst.get_pins("gnd")
for array_pin in array_pins:
supply_pin = self.top_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
if bottom:
inst = self.dummy_row_insts[0]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.bottom_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
# vdd/gnd are only connected in the perimeter cells
supply_insts = self.dummy_col_insts + self.dummy_row_insts
array_pins = inst.get_pins("gnd")
for array_pin in array_pins:
supply_pin = self.bottom_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
if left:
inst = self.dummy_col_insts[0]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.left_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
# For the wordlines
top_bot_mult = 1
left_right_mult = 1
# There are always vertical pins for the WLs on the left/right if we have unused wordlines
self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult)
self.right_gnd_locs = self.route_side_pin("gnd", "right", left_right_mult)
# This needs to be big enough so that they aren't in the same supply routing grid
left_right_mult = 4
if gnd_dir == "V":
self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult)
self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult)
# This needs to be big enough so that they aren't in the same supply routing grid
top_bot_mult = 4
if vdd_dir == "V":
self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult)
self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult)
elif vdd_dir == "H":
self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult)
self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult)
else:
debug.error("Invalid vdd direction {}".format(vdd_dir), -1)
for inst in supply_insts:
for pin in inst.get_pins("vdd"):
if vdd_dir == "V":
self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y)
self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y)
elif vdd_dir == "H":
self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x)
for inst in supply_insts:
for pin in inst.get_pins("gnd"):
if gnd_dir == "V":
self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y)
self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y)
elif gnd_dir == "H":
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
array_pins = inst.get_pins("gnd")
for array_pin in array_pins:
supply_pin = self.left_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
if right:
inst = self.dummy_col_insts[1]
if "vdd" in inst.mod.pins:
array_pins = inst.get_pins("vdd")
for array_pin in array_pins:
supply_pin = self.right_vdd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
array_pins = inst.get_pins("gnd")
for array_pin in array_pins:
supply_pin = self.right_gnd_pin
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
self.add_via_stack_center(from_layer = array_pin.layer,
to_layer = supply_pin.layer,
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
def route_unused_wordlines(self):
"""
Connect the unused RBL and dummy wordlines to gnd