From f729e9fca7baab046d196bc8955764a9a917fc72 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 20 Nov 2020 16:56:07 -0800 Subject: [PATCH] Fix new replica_bitcell_array refactor with end caps. Remove single port end cap exceptions. --- compiler/modules/replica_bitcell_array.py | 50 ++++------- compiler/modules/replica_column.py | 85 ++++++++++--------- .../14_replica_bitcell_array_1rw_1r_test.py | 8 +- .../tests/14_replica_column_1rw_1r_test.py | 18 ++-- compiler/tests/14_replica_column_test.py | 10 +-- 5 files changed, 77 insertions(+), 94 deletions(-) diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index ebfd06ba..d0ef36e6 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -318,14 +318,8 @@ class replica_bitcell_array(bitcell_base_array): self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) self.unused_offset = vector(self.unused_pitch, 0) - # Add extra width on the left and right for the unused WLs - self.height = (self.row_size + self.extra_rows) * self.dummy_row.height - self.width = (self.column_size + self.extra_cols) * self.cell.width + 2 * self.unused_pitch - - # This is a bitcell x bitcell offset to scale self.bitcell_offset = vector(self.cell.width, self.cell.height) - self.strap_offset = vector(0, 0) self.col_end_offset = vector(self.cell.width, self.cell.height) self.row_end_offset = vector(self.cell.width, self.cell.height) @@ -336,12 +330,15 @@ class replica_bitcell_array(bitcell_base_array): self.add_end_caps() - # 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 array_offset = self.bitcell_offset.scale(1 + len(self.left_rbl), 1 + self.rbl[0]) self.translate_all(array_offset.scale(-1, -1)) + # Add extra width on the left and right for the unused WLs + self.height = self.dummy_row_insts[1].uy() + self.width = self.dummy_col_insts[1].rx() + self.add_layout_pins() self.route_unused_wordlines() @@ -375,17 +372,17 @@ class replica_bitcell_array(bitcell_base_array): # Grow from left to right, toward the array for bit, port in enumerate(self.left_rbl): if not self.cell.end_caps: - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.strap_offset.scale(-len(self.left_rbl) + bit, 0) + self.unused_offset + offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + self.unused_offset else: - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(-len(self.left_rbl) + bit, 0) + self.unused_offset + offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.unused_offset self.replica_col_insts[bit].place(offset) # Grow to the right of the bitcell array, array outward for bit, port in enumerate(self.right_rbl): if not self.cell.end_caps: - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) + self.strap_offset.scale(bit, -self.rbl[0] - 1) + offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) else: - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.strap_offset.scale(bit, -self.rbl[0] - 1) + offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) self.replica_col_insts[self.rbl[0] + bit].place(offset) @@ -408,37 +405,24 @@ class replica_bitcell_array(bitcell_base_array): # FIXME: These depend on the array size itself # Far top dummy row (first row above array is NOT flipped) flip_dummy = self.rbl[1] % 2 - if not self.cell.end_caps: - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() - else: - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() - + dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1] + flip_dummy) + self.bitcell_array_inst.ul() self.dummy_row_insts[1].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.rbl[0] + 1) % 2 - if not self.cell.end_caps: - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset - else: - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - (self.col_end_offset.y/self.cell.height) + flip_dummy) + self.unused_offset + dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - 1 + flip_dummy) + self.unused_offset self.dummy_row_insts[0].place(offset=dummy_row_offset, - mirror="MX" if flip_dummy else "R0") + 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 - if not self.cell.end_caps: - dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset - else: - dummy_col_offset = self.bitcell_offset.scale(-(len(self.left_rbl)*(1+self.strap_offset.x/self.cell.width)) - (self.row_end_offset.x/self.cell.width), -len(self.left_rbl) - (self.col_end_offset.y/self.cell.height)) - + dummy_col_offset = self.bitcell_offset.scale(-len(self.left_rbl) - 1, -self.rbl[0] - 1) + self.unused_offset self.dummy_col_insts[0].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 - if not self.cell.end_caps: - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl), -self.rbl[0] - 1) + self.bitcell_array_inst.lr() - else: - dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl)*(1+self.strap_offset.x/self.cell.width), -self.rbl[0] - (self.col_end_offset.y/self.cell.height)) + self.bitcell_array_inst.lr() - + dummy_col_offset = self.bitcell_offset.scale(len(self.right_rbl), -self.rbl[0] - 1) + self.bitcell_array_inst.lr() self.dummy_col_insts[1].place(offset=dummy_col_offset) def add_layout_pins(self): @@ -455,6 +439,7 @@ class replica_bitcell_array(bitcell_base_array): offset=pin.ll().scale(0, 1), width=self.width, height=pin.height()) + # Replica wordlines (go by the row instead of replica column because we may have to add a pin # even though the column is in another local bitcell array) for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): @@ -549,8 +534,7 @@ class replica_bitcell_array(bitcell_base_array): 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, left_pin_loc]) - self.add_path(pin_layer, [right_loc, right_pin_loc]) + 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 f7e0c0fb..b43f2534 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -14,37 +14,38 @@ from tech import layer_properties as layer_props class replica_column(bitcell_base_array): """ Generate a replica bitline column for the replica array. - Rows is the total number of rows i the main array. + Rows is the total number of rows in the main array. rbl is a tuple with the number of left and right replica bitlines. Replica bit specifies which replica column this is (to determine where to put the replica cell relative to the bottom (including the dummy bit at 0). """ def __init__(self, name, rows, rbl, replica_bit, column_offset=0): - super().__init__(rows=sum(rbl) + rows + 2, cols=1, column_offset=column_offset, name=name) + # Used for pin names and properties + self.cell = factory.create(module_type=OPTS.bitcell) + # Row size is the number of rows with word lines + self.row_size = sum(rbl) + rows + # Start of regular word line rows + self.row_start = rbl[0] + 1 + # End of regular word line rows + self.row_end = self.row_start + rows + if not self.cell.end_caps: + self.row_size += 2 + super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name) self.rows = rows self.left_rbl = rbl[0] self.right_rbl = rbl[1] self.replica_bit = replica_bit - # left, right, regular rows plus top/bottom dummy cells - self.total_size = self.left_rbl + rows + self.right_rbl - - # Used for pin names and properties - self.cell = factory.create(module_type=OPTS.bitcell) - # For end caps - try: - if not self.cell.end_caps: - self.total_size += 2 - except AttributeError: - self.total_size += 2 + # Total size includes the replica rows and column cap rows + self.total_size = self.left_rbl + rows + self.right_rbl + 2 self.column_offset = column_offset - debug.check(replica_bit != 0 and replica_bit != rows, - "Replica bit cannot be the dummy row.") - debug.check(replica_bit <= self.left_rbl or replica_bit >= self.total_size - self.right_rbl - 1, + debug.check(replica_bit != 0 and replica_bit != self.total_size - 1, + "Replica bit cannot be the dummy/cap row.") + debug.check(replica_bit < self.row_start or replica_bit >= self.row_end, "Replica bit cannot be in the regular array.") if layer_props.replica_column.even_rows: debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, @@ -61,18 +62,20 @@ class replica_column(bitcell_base_array): self.create_instances() def create_layout(self): - self.height = self.total_size * self.cell.height - self.width = self.cell.width - self.place_instances() + + self.height = self.cell_inst[-1].uy() + self.width = self.cell_inst[0].rx() + self.add_layout_pins() + self.add_boundary() self.DRC_LVS() def add_pins(self): self.create_all_bitline_names() - self.create_all_wordline_names(self.total_size) + self.create_all_wordline_names(self.row_size) self.add_pin_list(self.all_bitline_names, "OUTPUT") self.add_pin_list(self.all_wordline_names, "INPUT") @@ -93,32 +96,32 @@ class replica_column(bitcell_base_array): self.add_mod(self.edge_cell) def create_instances(self): - self.cell_inst = {} + self.cell_inst = [] for row in range(self.total_size): name="rbc_{0}".format(row) - # Top/bottom cell are always dummy cells. - # Regular array cells are replica cells (>left_rbl and self.left_rbl and row < self.total_size - self.right_rbl - 1): - self.cell_inst[row]=self.add_inst(name=name, - mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) - elif row==self.replica_bit: - self.cell_inst[row]=self.add_inst(name=name, - mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) - elif (row == 0 or row == self.total_size - 1): - self.cell_inst[row]=self.add_inst(name=name, - mod=self.edge_cell) + real_row = row + if self.cell.end_caps: + real_row -= 1 + + # Regular array cells are replica cells + # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. + if (row == 0 or row == self.total_size - 1): + self.cell_inst.append(self.add_inst(name=name, + mod=self.edge_cell)) if self.cell.end_caps: - self.connect_inst(self.get_bitcell_pins_col_cap(row, 0)) + self.connect_inst(self.get_bitcell_pins_col_cap(real_row, 0)) else: - self.connect_inst(self.get_bitcell_pins(row, 0)) + self.connect_inst(self.get_bitcell_pins(real_row, 0)) + elif (row==self.replica_bit) or (row >= self.row_start and row < self.row_end): + self.cell_inst.append(self.add_inst(name=name, + mod=self.replica_cell)) + self.connect_inst(self.get_bitcell_pins(real_row, 0)) else: - self.cell_inst[row]=self.add_inst(name=name, - mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(row, 0)) + # Top/bottom cell are always dummy/cap cells. + self.cell_inst.append(self.add_inst(name=name, + mod=self.dummy_cell)) + self.connect_inst(self.get_bitcell_pins(real_row, 0)) def place_instances(self): # Flip the mirrors if we have an odd number of replica+dummy rows at the bottom @@ -184,7 +187,7 @@ class replica_column(bitcell_base_array): height=wl_pin.height()) # Supplies are only connected in the ends - for (index, inst) in self.cell_inst.items(): + for (index, inst) in enumerate(self.cell_inst): for pin_name in ["vdd", "gnd"]: if inst in [self.cell_inst[0], self.cell_inst[self.total_size - 1]]: self.copy_power_pins(inst, pin_name) 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 cf66692e..cbcb40d5 100755 --- a/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py +++ b/compiler/tests/14_replica_bitcell_array_1rw_1r_test.py @@ -25,14 +25,14 @@ class replica_bitcell_array_1rw_1r_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing 4x4 non-replica array for cell_1rw_1r") + debug.info(2, "Testing 4x4 non-replica array for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, rbl=[1, 1]) self.local_check(a) - debug.info(2, "Testing 4x4 left replica array for cell_1rw_1r") + debug.info(2, "Testing 4x4 left replica array for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, @@ -40,7 +40,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): left_rbl=[0]) self.local_check(a) - debug.info(2, "Testing 4x4 array left and right replica for cell_1rw_1r") + debug.info(2, "Testing 4x4 array left and right replica for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, @@ -52,7 +52,7 @@ class replica_bitcell_array_1rw_1r_test(openram_test): # Sky 130 has restrictions on the symmetries if OPTS.tech_name != "sky130": - debug.info(2, "Testing 4x4 array right only replica for cell_1rw_1r") + debug.info(2, "Testing 4x4 array right only replica for dp cell") a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, diff --git a/compiler/tests/14_replica_column_1rw_1r_test.py b/compiler/tests/14_replica_column_1rw_1r_test.py index d197a234..bf0a1108 100755 --- a/compiler/tests/14_replica_column_1rw_1r_test.py +++ b/compiler/tests/14_replica_column_1rw_1r_test.py @@ -25,18 +25,22 @@ class replica_column_test(openram_test): OPTS.num_w_ports = 0 globals.setup_bitcell() - debug.info(2, "Testing replica column for 6t_cell") + debug.info(2, "Testing one left replica column for dual port") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 0], replica_bit=1) self.local_check(a) - debug.info(2, "Testing replica column for 6t_cell") + debug.info(2, "Testing one right replica column for dual port") + a = factory.create(module_type="replica_column", rows=4, rbl=[0, 1], replica_bit=5) + self.local_check(a) + + debug.info(2, "Testing two (left, right) replica columns for dual port") + a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=1) + self.local_check(a) + + debug.info(2, "Testing two (left, right) replica columns for dual port") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6) self.local_check(a) - - debug.info(2, "Testing replica column for 6t_cell") - a = factory.create(module_type="replica_column", rows=4, rbl=[2, 0], replica_bit=2) - self.local_check(a) - + globals.end_openram() # run the test from the command line diff --git a/compiler/tests/14_replica_column_test.py b/compiler/tests/14_replica_column_test.py index 4701cdca..7d73524a 100755 --- a/compiler/tests/14_replica_column_test.py +++ b/compiler/tests/14_replica_column_test.py @@ -20,18 +20,10 @@ class replica_column_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - debug.info(2, "Testing replica column for cell_6t") + debug.info(2, "Testing replica column for single port") a = factory.create(module_type="replica_column", rows=4, rbl=[1, 0], replica_bit=1) self.local_check(a) - debug.info(2, "Testing replica column for cell_1rw_1r") - a = factory.create(module_type="replica_column", rows=4, rbl=[1, 1], replica_bit=6) - self.local_check(a) - - debug.info(2, "Testing replica column for cell_1rw_1r") - a = factory.create(module_type="replica_column", rows=4, rbl=[2, 0], replica_bit=2) - self.local_check(a) - globals.end_openram() # run the test from the command line