From 515591a42287ba8503647d041ea6c62924f12cb9 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Tue, 14 Apr 2026 14:48:26 -0700 Subject: [PATCH] dual port rba lvs clean again with cell library changes --- compiler/modules/bitcell_array.py | 23 +++++----- compiler/modules/bitcell_base_array.py | 16 ++++++- .../modules/capped_replica_bitcell_array.py | 2 +- compiler/modules/col_cap_array.py | 41 ++++++++++++------ compiler/modules/dummy_array.py | 21 +++++---- compiler/modules/pattern.py | 5 +-- compiler/modules/replica_bitcell_array.py | 14 +++--- compiler/modules/replica_column.py | 6 +-- compiler/modules/row_cap_array.py | 43 ++++++++++++++----- macros/sram_configs/sky130_sram_1rw_tiny.py | 1 + .../sky130/custom/sky130_bitcell_array.py | 4 +- technology/sky130/tech/tech.py | 2 +- 12 files changed, 116 insertions(+), 62 deletions(-) diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 6476c71a..264f9484 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -19,8 +19,8 @@ class bitcell_array(bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, name="", left_rbl=None, right_rbl=None): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + def __init__(self, rows, cols, column_offset=0, row_offset=0, name="", left_rbl=None, right_rbl=None): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -59,18 +59,21 @@ class bitcell_array(bitcell_base_array): self.cell = factory.create(module_type=OPTS.bitcell) def create_instances(self): + r = self.row_offset + c = self.column_offset self.cell_inst={} if self.cell.mirror.y: core_block = [[0 for x in range(2)] for y in range(2)] - core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - core_block[0][1] = geometry.instance("core_0_1", mod=self.cell, is_bitcell=True, mirror='MY') - core_block[1][1] = geometry.instance("core_1_1", mod=self.cell, is_bitcell=True, mirror='XY') + core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='XY') + core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='MX') + core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='MY') + core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='') else: - core_block = [[0 for x in range(1)] for y in range(2)] - core_block[0][0] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) - core_block[1][0] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') - + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True) + core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX') + print(r, c) + print(core_block) self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 7c42558f..8c6a813a 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -16,14 +16,14 @@ class bitcell_base_array(design): """ Abstract base class for bitcell-arrays -- bitcell, dummy, replica """ - def __init__(self, name, rows, cols, column_offset): + def __init__(self, name, rows, cols, column_offset, row_offset): super().__init__(name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.column_size = cols self.row_size = rows self.column_offset = column_offset - + self.row_offset = row_offset # Bitcell for port names only self.cell = factory.create(module_type=OPTS.bitcell) @@ -170,6 +170,18 @@ class bitcell_base_array(design): power_name = 'vdd' ground_name = 'gnd' + + if vdd_found == False or gnd_found == False: + for inst in self.insts: + if 'VDD' in inst.mod.get_pin_names(): + vdd_found = True + power_name = 'VDD' + + if 'GND' in inst.mod.get_pin_names(): + gnd_found = True + ground_name = 'GND' + + if vdd_found is False or gnd_found is False: from openram.tech import cell_properties try: diff --git a/compiler/modules/capped_replica_bitcell_array.py b/compiler/modules/capped_replica_bitcell_array.py index 78d3e927..eb0c68e1 100644 --- a/compiler/modules/capped_replica_bitcell_array.py +++ b/compiler/modules/capped_replica_bitcell_array.py @@ -20,7 +20,7 @@ class capped_replica_bitcell_array(bitcell_base_array): sides of a bitcell array. """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): - super().__init__(name, rows, cols, column_offset=0) + super().__init__(name, rows, cols, column_offset=0, row_offset=0) debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, rows, cols, diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index 510f7b0c..99940e98 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -8,12 +8,13 @@ from openram import OPTS from .bitcell_base_array import bitcell_base_array from openram.base import geometry from .pattern import pattern +from math import ceil class col_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name="",left_rbl=[],right_rbl=[]): + def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name="",left_rbl=[],right_rbl=[]): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.location = location @@ -38,28 +39,44 @@ class col_cap_array(bitcell_base_array): def create_layout(self): - self.place_array("dummy_r{0}_c{1}", self.mirror) + self.place_array() self.add_layout_pins() - self.height = self.dummy_cell.height - self.width = self.column_size * self.cell.width + #self.height = self.dummy_cell.height + #self.width = self.column_size * self.cell.width self.add_boundary() self.DRC_LVS() def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell)) + self.colend = factory.create(module_type="col_cap_{}".format(OPTS.bitcell)) + + # def create_instances(self): + # """ Create the module instances used in this design """ + # self.cell_inst = {} + # for col in range(self.column_size): + # for row in range(self.row_size): + # name = "bit_r{0}_c{1}".format(row, col) + # self.cell_inst[row, col]=self.add_inst(name=name, + # mod=self.dummy_cell) + # self.connect_inst(self.get_bitcell_pins(row, col)) def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - for col in range(self.column_size): - for row in range(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(row, col)) + self.cell_inst={} + if self.location == "top": + bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="MY")]\ + + [geometry.instance("01_colend", mod=self.colend, is_bitcell=True)] + elif self.location == "bottom": + bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="XY")]\ + + [geometry.instance("01_colend", mod=self.colend, is_bitcell=True, mirror="MX")] + + bit_row = pattern.rotate_list(bit_row, self.column_offset * 2) + bit_block = [] + pattern.append_row_to_block(bit_block, bit_row) + self.pattern = pattern(self, "col_cap_array_" + self.location , bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="col_cap_array" + self.location + "_r{0}_c{1}") + self.pattern.connect_array() def get_bitcell_pins(self, row, col): """ diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index 01472e1d..45b04c41 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -15,7 +15,7 @@ class dummy_array(bitcell_base_array): """ def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name="", left_rbl=0, right_rbl=0): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.location = location self.row_offset = row_offset self.mirror = mirror @@ -55,17 +55,20 @@ class dummy_array(bitcell_base_array): def create_instances(self): """ Create the module instances used in this design """ self.cell_inst={} + r = self.row_offset + c = self.column_offset if self.cell.mirror.y: core_block = [[0 for x in range(2)] for y in range(2)] - core_block[(0+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(1+self.mirror) %2][(0+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) - core_block[(0+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_0_1", mod=self.dummy_cell, is_bitcell=True, mirror='XY') - core_block[(1+self.mirror) %2][(1+self.column_offset+self.row_offset) %2] = geometry.instance("core_1_1", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='XY') + core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MY') + core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='') else: - core_block = [[0 for x in range(1)] for y in range(2)] - core_block[(1+self.mirror) %2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - core_block[(0+self.mirror) %2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True) - + core_block = [[0 for x in range(1)] for y in range(2)] + core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True) + core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') + print(r, c) + print(core_block) self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index 798ef331..72ef37de 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -147,7 +147,6 @@ class pattern(): def connect_array(self) -> None: - #debug_array = [[None]*12 for _ in range(6)] row = 0 col = 0 for i in range(self.num_cores_y): @@ -183,7 +182,7 @@ class pattern(): y += inst.height if "Y" in inst.mirror: x += inst.width - #print('placing inst {} at {}'.format(inst, offset)) + print('placing inst {} at {}'.format(inst, offset)) inst.place((x, y), inst.mirror, inst.rotate) def place_array(self): @@ -193,7 +192,7 @@ class pattern(): for row in range(self.row_max+1): x = 0 for col in range(self.col_max+1): - inst = self.parent_design.all_inst[self.row_max - row, col] + inst = self.parent_design.all_inst[row, col] self.place_inst(inst, (x, y)) x += inst.width y += inst.height diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 7262e023..d27fd2df 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -25,7 +25,7 @@ class replica_bitcell_array(bitcell_base_array): replica bitcell and dummy bitcell (BL/BR disconnected). """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): - super().__init__(name=name, rows=rows, cols=cols, column_offset=0) + super().__init__(name=name, rows=rows, cols=cols, column_offset=0, row_offset=0) debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, rows, cols, @@ -77,6 +77,7 @@ class replica_bitcell_array(bitcell_base_array): # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", column_offset=len(self.left_rbl), + row_offset=len(self.left_rbl), cols=self.column_size, rows=self.row_size, left_rbl=self.left_rbl, @@ -110,20 +111,17 @@ class replica_bitcell_array(bitcell_base_array): for port in self.all_ports: if port in self.left_rbl: - row_offset = self.row_size - mirror = row_offset % 2 + 1 - elif port in self.right_rbl: row_offset = 0 - mirror = 0 + elif port in self.right_rbl: + row_offset = self.row_size + len(self.left_rbl) else: - continue + row_offset = 0 self.dummy_rows[port] = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, row_offset=row_offset, - column_offset=len(self.left_rbl), - mirror=mirror) + column_offset=len(self.left_rbl)) def add_pins(self): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 433ac2ec..c07b039c 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -30,7 +30,7 @@ class replica_column(bitcell_base_array): self.row_start = rbl[0] # End of regular word line rows self.row_end = self.row_start + rows - super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name) + super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, row_offset=0, name=name) self.rows = rows self.left_rbl = rbl[0] @@ -95,12 +95,12 @@ class replica_column(bitcell_base_array): # Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell. # All other cells are dummies if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end): - if current_row % 2 == 1: + if current_row % 2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') else: - if current_row % 2 == 1: + if current_row % 2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True) else: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index c68034fe..813869e3 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -6,16 +6,19 @@ from openram.sram_factory import factory from openram import OPTS from .bitcell_base_array import bitcell_base_array - +from .pattern import pattern +from openram.base import geometry +from math import ceil class row_cap_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""): + def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name=""): super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.location = location + self.row_offset = row_offset #self.no_instances = True self.create_netlist() if not OPTS.netlist_only: @@ -33,7 +36,7 @@ class row_cap_array(bitcell_base_array): def create_layout(self): - self.place_array("dummy_r{0}_c{1}", self.mirror) + self.place_array() self.add_layout_pins() self.width = max([x.rx() for x in self.insts]) @@ -44,19 +47,37 @@ class row_cap_array(bitcell_base_array): def add_modules(self): """ Add the modules used in this design """ - self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell)) + self.row_cap = factory.create(module_type="row_cap_{}".format(OPTS.bitcell)) self.cell = factory.create(module_type=OPTS.bitcell) def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - for col in range(self.column_size): - for row in range(0, self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(row, col)) + self.all_inst={} + self.cell_inst={} + + bit_block = [] + + if self.location == "left": + #top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="MY") + #bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="XY") + rowend = geometry.instance("row_cap_rowend", mod=self.row_cap, is_bitcell=True, mirror="") + rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="MX") + elif self.location == "right": + #top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False) + #bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="MX") + rowend = geometry.instance("row_cap_rowend", mod=self.row_cap, is_bitcell=True, mirror="MY") + rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="XY") + #pattern.append_row_to_block(bit_block, [top_corner]) + for row in range(0, self.row_size): + if row % 2 == 1: + pattern.append_row_to_block(bit_block, [rowend]) + else: + pattern.append_row_to_block(bit_block, [rowend_m]) + + #pattern.append_row_to_block(bit_block, [bottom_corner]) + self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}") + self.pattern.connect_array_raw() def get_bitcell_pins(self, row, col): """ diff --git a/macros/sram_configs/sky130_sram_1rw_tiny.py b/macros/sram_configs/sky130_sram_1rw_tiny.py index 99781e1a..e04bb3a6 100644 --- a/macros/sram_configs/sky130_sram_1rw_tiny.py +++ b/macros/sram_configs/sky130_sram_1rw_tiny.py @@ -26,3 +26,4 @@ num_sim_threads = 1 #analytical_delay = False import os exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) +#tech_file = "tech_custom_cell" \ No newline at end of file diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index b6b7a84a..a6af455d 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -19,8 +19,8 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Creates a rows x cols array of memory cells. Assumes bit-lines and word lines are connected by abutment. """ - def __init__(self, rows, cols, column_offset=0, name="",left_rbl=None, right_rbl=None): - super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) + def __init__(self, rows, cols, column_offset=0, row_offset=0, name="",left_rbl=None, right_rbl=None): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name) self.left_rbl = left_rbl self.right_rbl = right_rbl diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 3aa4659f..e65a13db 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -15,5 +15,5 @@ sys.path.append("{}/{}".format(dir_path,'tech_configs')) if not hasattr(OPTS, 'tech_file'): OPTS.tech_file = 'tech_cypress_cell' -#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING +#TODO: FIX THIS TERRIBLE HACK JUST FOR TESTING EXECUTING exec('from {} import *'.format(OPTS.tech_file))