diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index d9fe78b5..a2bf8379 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -13,8 +13,8 @@ class col_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.no_instances = True diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py index 8b56ea19..d446d29c 100644 --- a/compiler/modules/local_bitcell_array.py +++ b/compiler/modules/local_bitcell_array.py @@ -6,7 +6,6 @@ # All rights reserved. # from bitcell_base_array import bitcell_base_array -from tech import drc, spice from globals import OPTS from sram_factory import factory diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 0c08cd56..a0ce8e6e 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -21,7 +21,7 @@ class replica_bitcell_array(design.design): Requires a regular bitcell array, replica bitcell, and dummy bitcell (Bl/BR disconnected). """ - def __init__(self, rows, cols, left_rbl, right_rbl, bitcell_ports, name): + def __init__(self, rows, cols, left_rbl, right_rbl, bitcell_ports, name, add_replica=True): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -31,15 +31,26 @@ class replica_bitcell_array(design.design): self.left_rbl = left_rbl self.right_rbl = right_rbl self.bitcell_ports = bitcell_ports + # If set to false, we increase the height for the replica wordline row, but don't + # actually add the column to this array. This is so the height matches other + # banks that have the replica columns. + # Number of replica columns to actually add + if add_replica: + self.add_left_rbl = self.left_rbl + self.add_right_rbl = self.right_rbl + else: + self.add_left_rbl = 0 + self.add_right_rbl = 0 debug.check(left_rbl + right_rbl == len(self.all_ports), "Invalid number of RBLs for port configuration.") debug.check(left_rbl + right_rbl == len(self.bitcell_ports), "Bitcell ports must match total RBLs.") - # Two dummy rows/cols plus replica for each port - self.extra_rows = 2 + left_rbl + right_rbl - self.extra_cols = 2 + left_rbl + right_rbl + # Two dummy rows plus replica even if we don't add the column + self.extra_rows = 2 + self.left_rbl + self.right_rbl + # Two dummy cols plus replica if we add the column + self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl self.create_netlist() if not OPTS.netlist_only: @@ -84,28 +95,28 @@ class replica_bitcell_array(design.design): # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=1 + self.left_rbl, + column_offset=1 + self.add_left_rbl, cols=self.column_size, rows=self.row_size) self.add_mod(self.bitcell_array) # Replica bitlines self.replica_columns = {} - for bit in range(self.left_rbl + self.right_rbl): + for bit in range(self.add_left_rbl + self.add_right_rbl): # Creating left_rbl - if bit1 port) - for port in range(self.left_rbl): + for port in range(self.add_left_rbl): # Make names for all RBLs wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL @@ -184,7 +195,7 @@ class replica_bitcell_array(design.design): # Regular WLs self.replica_col_wl_names.extend(self.bitcell_array_wl_names) # Right port WLs (one dummy for each port when we allow >1 port) - for port in range(self.left_rbl, self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl, self.add_left_rbl + self.add_right_rbl): # Make names for all RBLs wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] # Keep track of the pin that is the RBL @@ -192,14 +203,33 @@ class replica_bitcell_array(design.design): self.replica_col_wl_names.extend(wl_names) self.replica_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names]) + # Create the full WL names include dummy, replica, and regular bit cells # Left/right dummy columns are connected identically to the replica column - self.dummy_col_wl_names = self.replica_col_wl_names + self.dummy_col_wl_names = [] + self.dummy_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names]) + # Left port WLs (one dummy for each port when we allow >1 port) + for port in range(self.left_rbl): + # Make names for all RBLs + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] + # Keep track of the pin that is the RBL + self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] + self.dummy_col_wl_names.extend(wl_names) + # Regular WLs + self.dummy_col_wl_names.extend(self.bitcell_array_wl_names) + # Right port WLs (one dummy for each port when we allow >1 port) + for port in range(self.left_rbl, self.left_rbl + self.right_rbl): + # Make names for all RBLs + wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))] + # Keep track of the pin that is the RBL + self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]] + self.dummy_col_wl_names.extend(wl_names) + self.dummy_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names]) # Per port bitline names self.replica_bl_names = {} self.replica_wl_names = {} # Array of all port bitline names - for port in range(self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl + self.add_right_rbl): left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))] right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))] # Keep track of the left pins that are the RBL @@ -209,6 +239,8 @@ class replica_bitcell_array(design.design): bl_names = [x for t in zip(left_names, right_names) for x in t] self.replica_bl_names[port] = bl_names + # Array of all port wl names + for port in range(self.left_rbl + self.right_rbl): wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()] self.replica_wl_names[port] = wl_names @@ -243,13 +275,14 @@ class replica_bitcell_array(design.design): # Replica columns self.replica_col_inst = {} - for port in range(self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl + self.add_right_rbl): self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), mod=self.replica_columns[port]) self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) - + # Dummy rows under the bitcell array (connected with with the replica cell wl) self.dummy_row_replica_inst = {} + # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! for port in range(self.left_rbl + self.right_rbl): self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), mod=self.dummy_row) @@ -277,41 +310,18 @@ class replica_bitcell_array(design.design): self.width = (self.column_size + self.extra_cols) * self.cell.width # This is a bitcell x bitcell offset to scale - offset = vector(self.cell.width, self.cell.height) + self.bitcell_offset = vector(self.cell.width, self.cell.height) + # Everything is computed with the main array at (0, 0) to start self.bitcell_array_inst.place(offset=[0, 0]) - # To the left of the bitcell array - for bit in range(self.left_rbl): - self.replica_col_inst[bit].place(offset=offset.scale(-bit - 1, -self.left_rbl - 1)) - # To the right of the bitcell array - for bit in range(self.right_rbl): - self.replica_col_inst[self.left_rbl + bit].place(offset=offset.scale(bit, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) + self.add_replica_columns() + + self.add_end_caps() - # FIXME: These depend on the array size itself - # Far top dummy row (first row above array is NOT flipped) - flip_dummy = self.right_rbl % 2 - self.dummy_row_top_inst.place(offset=offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul(), - mirror="MX" if flip_dummy else "R0") - # FIXME: These depend on the array size itself - # Far bottom dummy row (first row below array IS flipped) - flip_dummy = (self.left_rbl + 1) % 2 - self.dummy_row_bot_inst.place(offset=offset.scale(0, -self.left_rbl - 1 + flip_dummy), - mirror="MX" if flip_dummy else "R0") - # Far left dummy col - self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl - 1, -self.left_rbl - 1)) - # Far right dummy col - self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()) - - # Replica dummy rows - for bit in range(self.left_rbl): - self.dummy_row_replica_inst[bit].place(offset=offset.scale(0, -bit - bit % 2), - mirror="R0" if bit % 2 else "MX") - for bit in range(self.right_rbl): - self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), - mirror="MX" if bit % 2 else "R0") - - self.translate_all(offset.scale(-1 - self.left_rbl, -1 - self.left_rbl)) + # Array was at (0, 0) but move everything so it is at the lower left + # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array + self.translate_all(self.bitcell_offset.scale(-1 - self.add_left_rbl, -1 - self.left_rbl)) self.add_layout_pins() @@ -319,6 +329,49 @@ class replica_bitcell_array(design.design): self.DRC_LVS() + def add_replica_columns(self): + """ Add replica columns on left and right of array """ + + # To the left of the bitcell array + for bit in range(self.add_left_rbl): + self.replica_col_inst[bit].place(offset=self.bitcell_offset.scale(-bit - 1, -self.add_left_rbl - 1)) + # To the right of the bitcell array + for bit in range(self.add_right_rbl): + self.replica_col_inst[self.add_left_rbl + bit].place(offset=self.bitcell_offset.scale(bit, -self.add_left_rbl - 1) + self.bitcell_array_inst.lr()) + + # Replica dummy rows + # Add the dummy rows even if we aren't adding the replica column to this bitcell array + for bit in range(self.left_rbl): + self.dummy_row_replica_inst[bit].place(offset=self.bitcell_offset.scale(0, -bit - bit % 2), + mirror="R0" if bit % 2 else "MX") + for bit in range(self.right_rbl): + self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(), + mirror="MX" if bit % 2 else "R0") + + def add_end_caps(self): + """ Add dummy cells or end caps around the array """ + + # FIXME: These depend on the array size itself + # Far top dummy row (first row above array is NOT flipped) + flip_dummy = self.right_rbl % 2 + dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul() + self.dummy_row_top_inst.place(offset=dummy_row_offset, + mirror="MX" if flip_dummy else "R0") + # FIXME: These depend on the array size itself + # Far bottom dummy row (first row below array IS flipped) + flip_dummy = (self.left_rbl + 1) % 2 + dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - 1 + flip_dummy) + self.dummy_row_bot_inst.place(offset=dummy_row_offset, + mirror="MX" if flip_dummy else "R0") + # 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(-self.add_left_rbl - 1, -self.left_rbl - 1) + self.dummy_col_left_inst.place(offset=dummy_col_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(self.add_right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr() + self.dummy_col_right_inst.place(offset=dummy_col_offset) + def add_layout_pins(self): """ Add the layout pins """ @@ -345,13 +398,13 @@ class replica_bitcell_array(design.design): height=self.height) # Replica wordlines - for port in range(self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl + self.add_right_rbl): inst = self.replica_col_inst[port] for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wl_names[port]): # +1 for dummy row pin_bit = port + 1 # +row_size if above the array - if port>=self.left_rbl: + if port>=self.add_left_rbl: pin_bit += self.row_size pin_name += "_{}".format(pin_bit) @@ -362,12 +415,13 @@ class replica_bitcell_array(design.design): offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) - + # Replica bitlines - for port in range(self.left_rbl + self.right_rbl): + for port in range(self.add_left_rbl + self.add_right_rbl): inst = self.replica_col_inst[port] for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bl_names[port]): pin = inst.get_pin(pin_name) + if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names: name = bl_name else: diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index bfbd035c..58b33c89 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -13,8 +13,8 @@ class row_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.no_instances = True self.create_netlist() diff --git a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py index 626725c4..bfb35cd7 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -24,14 +24,35 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1]) + debug.info(2, "Testing 4x4 non-replica array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + left_rbl=1, + right_rbl=1, + bitcell_ports=[0, 1], + add_replica=False) self.local_check(a) + debug.info(2, "Testing 4x4 array for cell_1rw_1r") + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + left_rbl=1, + right_rbl=1, + bitcell_ports=[0, 1]) + self.local_check(a) + + # Sky 130 has restrictions on the symmetries if OPTS.tech_name != "sky130": debug.info(2, "Testing 4x4 array for cell_1rw_1r") - a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) + a = factory.create(module_type="replica_bitcell_array", + cols=4, + rows=4, + left_rbl=2, + right_rbl=0, + bitcell_ports=[0, 1]) self.local_check(a) globals.end_openram()