diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 87f9f149..16e70bb0 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -158,9 +158,10 @@ class bitcell_base_array(design): height=wl_pin.height()) def route_supplies(self): - for inst in self.cell_inst.values(): + for inst in self.insts: for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) + if pin_name in inst.mod.get_pin_names(): + self.copy_layout_pin(inst, pin_name) def add_layout_pins(self): """ Add the layout pins """ diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index b31f8c00..fbf79cd9 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -64,10 +64,9 @@ class dummy_array(bitcell_base_array): core_block[(1+self.mirror) %2][0] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX') - 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 = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size * 2, name_template="bit_r{0}_c{1}") self.pattern.connect_array() - def add_pins(self): # bitline pins are not added because they are floating for bl_name in self.get_bitline_names(): @@ -78,47 +77,6 @@ class dummy_array(bitcell_base_array): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def add_layout_pins(self): - """ Add the layout pins """ - - # Add the bitline metal, but not as pins since they are going to just be floating - # For some reason, LVS has an issue if we don't add this metal - bitline_names = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for port in self.all_ports: - bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) - self.add_layout_pin(text="bl_{0}_{1}".format(port, col), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) - self.add_layout_pin(text="br_{0}_{1}".format(port, col), - layer=br_pin.layer, - offset=br_pin.ll().scale(1, 0), - width=br_pin.width(), - height=self.height) - - wl_names = self.cell.get_all_wl_names() - for row in range(self.row_size): - for port in self.all_ports: - wl_pins = self.cell_inst[row, 0].get_pins(wl_names[port]) - for wl_pin in wl_pins: - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) - - def route_supplies(self): - - # Copy a vdd/gnd layout pin from every cell - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) - def input_load(self): # FIXME: This appears to be old code from previous characterization. Needs to be updated. wl_wire = self.gen_wl_wire() diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index a7cc062f..c52d6480 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -122,7 +122,7 @@ class pattern(): for dr in range(len(block)): row_done = False for dc in range(len(block[0])): - if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.bit_cols) <= self.num_rows): + if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.num_cols) <= self.num_rows): inst = block[dr][dc] if(len(self.bit_rows) <= col + dc): self.bit_rows.append(0) @@ -211,3 +211,11 @@ class pattern(): def append_block_right_block(base_block, right_block): for row in base_block: row = row + right_block + + def rotate_list(lst, n): + # Loop through the range of n positions to rotate + for i in range(n): + # Remove the last element of the list and insert it at the beginning + lst.insert(0, lst.pop()) + # Return the rotated list + return lst diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index b3661aff..805b39f0 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -74,7 +74,7 @@ class replica_bitcell_array(bitcell_base_array): """ Array and dummy/replica columns """ # Bitcell array self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=1 + len(self.left_rbl), + column_offset=len(self.left_rbl), cols=self.column_size, rows=self.row_size) @@ -91,7 +91,7 @@ class replica_bitcell_array(bitcell_base_array): elif port in self.right_rbl: # These go bottom up starting from the top of the bitcell array. replica_bit = self.rbl[0] + self.row_size + port - 1 - column_offset = len(self.left_rbl) + self.column_size + 1 + column_offset = len(self.left_rbl) + self.column_size else: continue @@ -102,14 +102,21 @@ class replica_bitcell_array(bitcell_base_array): replica_bit=replica_bit) # Dummy row (for replica wordlines) - self.dummy_row = factory.create(module_type="dummy_array", + self.dummy_rows = {} + + for port in self.all_ports: + if port in self.left_rbl: + row_offset = self.row_size + elif port in self.right_rbl: + row_offset = 0 + else: + continue + self.dummy_rows[port] = factory.create(module_type="dummy_array", cols=self.column_size, rows=1, - # cap column + left replica column - # FIXME: these col offsets should really start at 0 because - # this is the left edge of the array... but changing them all is work - column_offset=1 + len(self.left_rbl), - mirror=0) + row_offset=row_offset, + column_offset=len(self.left_rbl), + mirror='R0') def add_pins(self): @@ -213,7 +220,7 @@ class replica_bitcell_array(bitcell_base_array): for port in self.all_ports: # TODO: tie to self.rbl or whatever if self.rbl[port] != 0: self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), - mod=self.dummy_row)) + mod=self.dummy_rows[port])) self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + self.supplies) else: self.dummy_row_replica_insts.append(None) @@ -244,11 +251,13 @@ class replica_bitcell_array(bitcell_base_array): self.add_layout_pins() self.route_supplies() + + ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts])) - self.height = (sum(self.rbl) + self.row_size) * self.cell.height - self.width = (len(self.rbls) + self.column_size) * self.cell.width + self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts]) + self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts]) - self.add_boundary() + self.add_boundary(ll) self.DRC_LVS() @@ -280,25 +289,24 @@ class replica_bitcell_array(bitcell_base_array): # Grow from left to right, toward the array for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0]) + rbc_dimension = vector(self.replica_col_insts[port].width, self.cell.height) + offset = rbc_dimension.scale(-len(self.left_rbl) + bit, -self.rbl[0]) 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): - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0]) + offset = self.bitcell_array_inst.lr() + rbc_dimension.scale(bit, -self.rbl[0]) self.replica_col_insts[self.rbl[0] + bit].place(offset) # Replica dummy rows # Add the dummy rows even if we aren't adding the replica column to this bitcell array # These grow up, toward the array for bit in range(self.rbl[0]): - dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) - self.dummy_row_replica_insts[bit].place(offset=dummy_offset, - mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") + dummy_offset = self.bitcell_array_inst.ll() - vector(0, self.dummy_row_replica_insts[bit].height) + self.dummy_row_replica_insts[bit].place(offset=dummy_offset) # These grow up, away from the array for bit in range(self.rbl[1]): - dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, - mirror="MX" if (self.row_size + bit) % 2 else "R0") + dummy_offset = self.bitcell_array_inst.ul() + self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset) def add_layout_pins(self): """ Add the layout pins """ @@ -317,7 +325,7 @@ class replica_bitcell_array(bitcell_base_array): # 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): - for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): + for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()): pin = inst.get_pin(pin_name) self.add_layout_pin(text=wl_name, layer=pin.layer, @@ -347,12 +355,6 @@ class replica_bitcell_array(bitcell_base_array): width=pin.width(), height=self.height) - def route_supplies(self): - """ just copy supply pins from all instances """ - for inst in self.insts: - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) - def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" # Dynamic Power from Bitline diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index dc1b6f12..1645442e 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -45,10 +45,10 @@ class replica_column(bitcell_base_array): 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, - "sky130 currently requires rows to be even and to start with X mirroring" - + " (left_rbl must be odd) for LVS.") + #if layer_props.replica_column.even_rows: + # debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0, + # "sky130 currently requires rows to be even and to start with X mirroring" + # + " (left_rbl must be odd) for LVS.") self.create_netlist() if not OPTS.netlist_only: @@ -95,63 +95,19 @@ 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: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') - else: + if current_row % 2 == 0: core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True) - else: - if current_row %2: - core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MX') else: + core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MX') + else: + 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') current_row += 1 self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size, name_template="rbc_r{0}_c{1}") self.pattern.connect_array() - def add_layout_pins(self): - for port in self.all_ports: - bl_pin = self.cell_inst[0,0].get_pin(self.cell.get_bl_name(port)) - self.add_layout_pin(text="bl_{0}_{1}".format(port, 0), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - bl_pin = self.cell_inst[0,0].get_pin(self.cell.get_br_name(port)) - self.add_layout_pin(text="br_{0}_{1}".format(port, 0), - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - - for port in self.all_ports: - for row in range(self.total_size): - wl_pin = self.cell_inst[row,0].get_pin(self.cell.get_wl_name(port)) - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) - - def get_bitline_names(self, port=None): - if port == None: - return self.all_bitline_names - else: - return self.bitline_names[port] - - def get_bitcell_pins(self, row, col): - """ - Creates a list of connections in the bitcell, - indexed by column and row, for instance use in bitcell_array - """ - bitcell_pins = [] - for port in self.all_ports: - bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))]) - bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))]) - bitcell_pins.append("vdd") - bitcell_pins.append("gnd") - - return bitcell_pins - def get_bitcell_pins_col_cap(self, row, col): """ Creates a list of connections in the bitcell, diff --git a/technology/sky130/custom/replica_bitcell_array.py b/technology/sky130/custom/replica_bitcell_array.py deleted file mode 100644 index 2b1ab54a..00000000 --- a/technology/sky130/custom/replica_bitcell_array.py +++ /dev/null @@ -1,731 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz -# All rights reserved. -# -from openram import debug -from openram.base import vector -from openram.base import contact -from openram.sram_factory import factory -from openram.tech import drc, spice -from openram.tech import cell_properties as props -from openram import OPTS -from openram.modules import bitcell_base_array - - -class replica_bitcell_array(bitcell_base_array): - """ - Creates a bitcell array of cols x rows and then adds the replica - and dummy columns and rows. Replica columns are on the left and - right, respectively and connected to the given bitcell ports. - Dummy are the outside columns/rows with WL and BL tied to gnd. - Requires a regular bitcell 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) - debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name, - rows, - cols, - rbl, - left_rbl, - right_rbl)) - self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl)) - - self.column_size = cols - self.row_size = rows - # This is how many RBLs are in all the arrays - if rbl: - self.rbl = rbl - else: - self.rbl=[1, 1 if len(self.all_ports)>1 else 0] - # This specifies which RBL to put on the left or right - # by port number - # This could be an empty list - if left_rbl != None: - self.left_rbl = left_rbl - else: - self.left_rbl = [0] - # This could be an empty list - if right_rbl != None: - self.right_rbl = right_rbl - else: - self.right_rbl=[1] if len(self.all_ports) > 1 else [] - self.rbls = self.left_rbl + self.right_rbl - - debug.check(sum(self.rbl) == len(self.all_ports), - "Invalid number of RBLs for port configuration.") - debug.check(sum(self.rbl) >= len(self.left_rbl) + len(self.right_rbl), - "Invalid number of RBLs for port configuration.") - - # Two dummy rows plus replica even if we don't add the column - self.extra_rows = sum(self.rbl) - # Two dummy cols plus replica if we add the column - self.extra_cols = len(self.left_rbl) + len(self.right_rbl) - - # 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 - self.extra_cols += 2 - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - # We don't offset this because we need to align - # the replica bitcell in the control logic - # self.offset_all_coordinates() - - def create_netlist(self): - """ Create and connect the netlist """ - self.add_modules() - self.add_pins() - self.create_instances() - - def add_modules(self): - """ Array and dummy/replica columns - - d or D = dummy cell (caps to distinguish grouping) - r or R = replica cell (caps to distinguish grouping) - b or B = bitcell - replica columns 1 - v v - bdDDDDDDDDDDDDDDdb <- Dummy row - bdDDDDDDDDDDDDDDrb <- Dummy row - br--------------rb - br| Array |rb - br| row x col |rb - br--------------rb - brDDDDDDDDDDDDDDdb <- Dummy row - bdDDDDDDDDDDDDDDdb <- Dummy row - - ^^^^^^^^^^^^^^^ - dummy rows cols x 1 - - ^ dummy columns ^ - 1 x (rows + 4) - """ - # Bitcell array - self.bitcell_array = factory.create(module_type="bitcell_array", - column_offset=1 + len(self.left_rbl), - cols=self.column_size, - rows=self.row_size) - - # Replica bitlines - self.replica_columns = {} - - for port in self.all_ports: - if port in self.left_rbl: - # We will always have self.rbl[0] rows of replica wordlines below - # the array. - # These go from the top (where the bitcell array starts ) down - replica_bit = self.rbl[0] - port - column_offset = self.rbl[0] - - elif port in self.right_rbl: - - # We will always have self.rbl[0] rows of replica wordlines below - # the array. - # These go from the bottom up - replica_bit = self.rbl[0] + self.row_size + port - column_offset = self.rbl[0] + self.column_size + 1 - else: - continue - - self.replica_columns[port] = factory.create(module_type="replica_column", - rows=self.row_size, - rbl=self.rbl, - column_offset=column_offset, - replica_bit=replica_bit) - - # Dummy row - self.dummy_row = factory.create(module_type="dummy_array", - cols=self.column_size, - rows=1, - # dummy column + left replica column - column_offset=1 + len(self.left_rbl), - mirror=0) - - # Dummy Row or Col Cap, depending on bitcell array properties - col_cap_module_type = ("col_cap_array" if self.cell.end_caps else "dummy_array") - self.col_cap_top = factory.create(module_type=col_cap_module_type, - cols=self.column_size, - rows=1, - # dummy column + left replica column(s) - column_offset=1 + len(self.left_rbl), - mirror=0, - location="top") - - self.col_cap_bottom = factory.create(module_type=col_cap_module_type, - cols=self.column_size, - rows=1, - # dummy column + left replica column(s) - column_offset=1 + len(self.left_rbl), - mirror=0, - location="bottom") - - # Dummy Col or Row Cap, depending on bitcell array properties - row_cap_module_type = ("row_cap_array" if self.cell.end_caps else "dummy_array") - - self.row_cap_left = factory.create(module_type=row_cap_module_type, - cols=1, - column_offset=0, - rows=self.row_size + self.extra_rows, - mirror=(self.rbl[0] + 1) % 2) - - self.row_cap_right = factory.create(module_type=row_cap_module_type, - cols=1, - # dummy column - # + left replica column(s) - # + bitcell columns - # + right replica column(s) - column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0], - rows=self.row_size + self.extra_rows, - mirror=(self.rbl[0] + 1) %2) - - def add_pins(self): - - # Arrays are always: - # bitlines (column first then port order) - # word lines (row first then port order) - # dummy wordlines - # replica wordlines - # regular wordlines (bottom to top) - # # dummy bitlines - # replica bitlines (port order) - # regular bitlines (left to right port order) - # - # vdd - # gnd - - self.add_bitline_pins() - self.add_wordline_pins() - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - def add_bitline_pins(self): - # The bit is which port the RBL is for - for bit in self.rbls: - for port in self.all_ports: - self.rbl_bitline_names[bit].append("rbl_bl_{0}_{1}".format(port, bit)) - for port in self.all_ports: - self.rbl_bitline_names[bit].append("rbl_br_{0}_{1}".format(port, bit)) - # Make a flat list too - self.all_rbl_bitline_names = [x for sl in self.rbl_bitline_names for x in sl] - - self.bitline_names = self.bitcell_array.bitline_names - # Make a flat list too - self.all_bitline_names = [x for sl in zip(*self.bitline_names) for x in sl] - - for port in self.left_rbl: - self.add_pin_list(self.rbl_bitline_names[port], "INOUT") - self.add_pin_list(self.all_bitline_names, "INOUT") - for port in self.right_rbl: - self.add_pin_list(self.rbl_bitline_names[port], "INOUT") - - def add_wordline_pins(self): - - # Wordlines to ground - self.gnd_wordline_names = [] - - for port in self.all_ports: - for bit in self.all_ports: - self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) - if bit != port: - self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit)) - - self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl] - - self.wordline_names = self.bitcell_array.wordline_names - self.all_wordline_names = self.bitcell_array.all_wordline_names - - # All wordlines including dummy and RBL - self.replica_array_wordline_names = [] - self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - for bit in range(self.rbl[0]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]]) - self.replica_array_wordline_names.extend(self.all_wordline_names) - for bit in range(self.rbl[1]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]]) - self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - - for port in range(self.rbl[0]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - self.add_pin_list(self.all_wordline_names, "INPUT") - for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - - def create_instances(self): - """ Create the module instances used in this design """ - self.supplies = ["vdd", "gnd"] - - # Used for names/dimensions only - self.cell = factory.create(module_type=OPTS.bitcell) - - # Main array - self.bitcell_array_inst=self.add_inst(name="bitcell_array", - mod=self.bitcell_array) - self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies) - - # Replica columns - self.replica_col_insts = [] - for port in self.all_ports: - if port in self.rbls: - self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), - mod=self.replica_columns[port])) - self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies) - else: - self.replica_col_insts.append(None) - - # Dummy rows under the bitcell array (connected with with the replica cell wl) - self.dummy_row_replica_insts = [] - # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! - for port in self.all_ports: - self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), - mod=self.dummy_row)) - self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies) - - # Top/bottom dummy rows or col caps - self.dummy_row_insts = [] - self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", - mod=self.col_cap_bottom)) - self.connect_inst(self.all_bitline_names + ["gnd"] * len(self.col_cap_bottom.get_wordline_names()) + self.supplies) - self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", - mod=self.col_cap_top)) - self.connect_inst(self.all_bitline_names + ["gnd"] * len(self.col_cap_top.get_wordline_names()) + self.supplies) - - # Left/right Dummy columns - self.dummy_col_insts = [] - self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", - mod=self.row_cap_left)) - self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.replica_array_wordline_names + self.supplies) - self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", - mod=self.row_cap_right)) - self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + self.replica_array_wordline_names + self.supplies) - - def create_layout(self): - - # This creates space for the unused wordline connections as well as the - # 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])) - 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 main array - self.bitcell_array_inst.place(offset=self.unused_offset) - - self.add_replica_columns() - - 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 - # Note that this doesn't include the row/col cap - 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.width = self.dummy_col_insts[1].rx() + self.unused_offset.x - self.height = self.dummy_row_insts[1].uy() - - self.add_layout_pins() - - 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.add_boundary() - - self.DRC_LVS() - - def get_main_array_top(self): - """ Return the top of the main bitcell array. """ - return self.bitcell_array_inst.uy() - - def get_main_array_bottom(self): - """ Return the bottom of the main bitcell array. """ - return self.bitcell_array_inst.by() - - def get_main_array_left(self): - """ Return the left of the main bitcell array. """ - return self.bitcell_array_inst.lx() - - def get_main_array_right(self): - """ Return the right of the main bitcell array. """ - return self.bitcell_array_inst.rx() - - def get_replica_top(self): - """ Return the top of all replica columns. """ - return self.dummy_row_insts[0].by() - - def get_replica_bottom(self): - """ Return the bottom of all replica columns. """ - return self.dummy_row_insts[0].uy() - - def get_replica_left(self): - """ Return the left of all replica columns. """ - return self.dummy_col_insts[0].rx() - - def get_replica_right(self): - """ Return the right of all replica columns. """ - return self.dummy_col_insts[1].rx() - - def get_column_offsets(self): - """ - Return an array of the x offsets of all the regular bits - """ - offsets = [x + self.bitcell_array_inst.lx() for x in self.bitcell_array.get_column_offsets()] - return offsets - - def add_replica_columns(self): - """ Add replica columns on left and right of array """ - - # Grow from left to right, toward the array - for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0] - 1) + 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): - offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0] - 1) - self.replica_col_insts[self.rbl[0] + bit].place(offset) - - # Replica dummy rows - # Add the dummy rows even if we aren't adding the replica column to this bitcell array - # These grow up, toward the array - for bit in range(self.rbl[0]): - dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset - self.dummy_row_replica_insts[bit].place(offset=dummy_offset, - mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") - # These grow up, away from the array - for bit in range(self.rbl[1]): - dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, - mirror="MX" if (self.row_size + bit) % 2 else "R0") - - 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, 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") - - # 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, -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") - # 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(-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 - 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): - """ Add the layout pins """ - - # All wordlines - # Main array wl and bl/br - for pin_name in self.all_wordline_names: - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - 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): - for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): - if wl_name in self.gnd_wordline_names: - continue - pin = inst.get_pin(pin_name) - self.add_layout_pin(text=wl_name, - layer=pin.layer, - offset=pin.ll().scale(0, 1), - width=self.width, - height=pin.height()) - - for pin_name in self.all_bitline_names: - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - - # Replica bitlines - if len(self.rbls) > 0: - for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): - pin_names = self.replica_columns[self.rbls[0]].all_bitline_names - for (bl_name, pin_name) in zip(names, pin_names): - pin = inst.get_pin(pin_name) - self.add_layout_pin(text=bl_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - - def route_supplies(self): - - if OPTS.bitcell == "pbitcell": - bitcell = factory.create(module_type="pbitcell") - else: - bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports)) - - vdd_dir = bitcell.vdd_dir - gnd_dir = bitcell.gnd_dir - - # vdd/gnd are only connected in the perimeter cells - supply_insts = self.dummy_col_insts + self.dummy_row_insts - - # 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) - - - def route_unused_wordlines(self): - """ Connect the unused RBL and dummy wordlines to gnd """ - # This grounds all the dummy row word lines - for inst in self.dummy_row_insts: - for wl_name in self.col_cap_top.get_wordline_names(): - pin = inst.get_pin(wl_name) - self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) - self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) - - # Ground the unused replica wordlines - for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): - for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): - if wl_name in self.gnd_wordline_names: - pin = inst.get_pin(pin_name) - self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x) - self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x) - - def route_side_pin(self, name, side, offset_multiple=1): - """ - Routes a vertical or horizontal pin on the side of the bbox. - The multiple specifies how many track offsets to be away from the side assuming - (0,0) (self.width, self.height) - """ - if side in ["left", "right"]: - return self.route_vertical_side_pin(name, side, offset_multiple) - elif side in ["top", "bottom", "bot"]: - return self.route_horizontal_side_pin(name, side, offset_multiple) - else: - debug.error("Invalid side {}".format(side), -1) - - def route_vertical_side_pin(self, name, side, offset_multiple=1): - """ - Routes a vertical pin on the side of the bbox. - """ - if side == "left": - bot_loc = vector(-offset_multiple * self.vertical_pitch, 0) - top_loc = vector(-offset_multiple * self.vertical_pitch, self.height) - elif side == "right": - bot_loc = vector(self.width + offset_multiple * self.vertical_pitch, 0) - top_loc = vector(self.width + offset_multiple * self.vertical_pitch, self.height) - - layer = self.supply_stack[2] - top_via = contact(layer_stack=self.supply_stack, - directions=("H", "H")) - - -# self.add_layout_pin_rect_ends(text=name, -# layer=layer, -# start=bot_loc, -# end=top_loc) - self.add_layout_pin_segment_center(text=name, - layer=layer, - start=bot_loc, - end=top_loc, - width=top_via.second_layer_width) - - return (bot_loc, top_loc) - - def route_horizontal_side_pin(self, name, side, offset_multiple=1): - """ - Routes a horizontal pin on the side of the bbox. - """ - if side in ["bottom", "bot"]: - left_loc = vector(0, -offset_multiple * self.horizontal_pitch) - right_loc = vector(self.width, -offset_multiple * self.horizontal_pitch) - elif side == "top": - left_loc = vector(0, self.height + offset_multiple * self.horizontal_pitch) - right_loc = vector(self.width, self.height + offset_multiple * self.horizontal_pitch) - - layer = self.supply_stack[0] - side_via = contact(layer_stack=self.supply_stack, - directions=("V", "V")) - -# self.add_layout_pin_rect_ends(text=name, -# layer=layer, -# start=left_loc, -# end=right_loc) - self.add_layout_pin_segment_center(text=name, - layer=layer, - start=left_loc, - end=right_loc, - width=side_via.first_layer_height) - - return (left_loc, right_loc) - - def connect_side_pin(self, pin, side, offset): - """ - Used to connect horizontal layers of pins to the left/right straps - locs provides the offsets of the pin strip end points. - """ - if side in ["left", "right"]: - self.connect_vertical_side_pin(pin, side, offset) - elif side in ["top", "bottom", "bot"]: - self.connect_horizontal_side_pin(pin, side, offset) - else: - debug.error("Invalid side {}".format(side), -1) - - def connect_horizontal_side_pin(self, pin, side, yoffset): - """ - Used to connect vertical layers of pins to the top/bottom horizontal straps - """ - cell_loc = pin.center() - pin_loc = vector(cell_loc.x, yoffset) - - # Place the pins a track outside of the array - self.add_via_stack_center(offset=pin_loc, - from_layer=pin.layer, - to_layer=self.supply_stack[0], - directions=("V", "V")) - - # Add a path to connect to the array - self.add_path(pin.layer, [cell_loc, pin_loc]) - - - def connect_vertical_side_pin(self, pin, side, xoffset): - """ - Used to connect vertical layers of pins to the top/bottom vertical straps - """ - cell_loc = pin.center() - pin_loc = vector(xoffset, cell_loc.y) - - # Place the pins a track outside of the array - self.add_via_stack_center(offset=pin_loc, - from_layer=pin.layer, - to_layer=self.supply_stack[2], - directions=("H", "H")) - - # Add a path to connect to the array - self.add_path(pin.layer, [cell_loc, pin_loc]) - - def analytical_power(self, corner, load): - """Power of Bitcell array and bitline in nW.""" - # Dynamic Power from Bitline - bl_wire = self.gen_bl_wire() - cell_load = 2 * bl_wire.return_input_cap() - bl_swing = OPTS.rbl_delay_percentage - freq = spice["default_event_frequency"] - bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) - - # Calculate the bitcell power which currently only includes leakage - cell_power = self.cell.analytical_power(corner, load) - - # Leakage power grows with entire array and bitlines. - total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, - cell_power.leakage * self.column_size * self.row_size) - return total_power - - - def gen_bl_wire(self): - if OPTS.netlist_only: - height = 0 - else: - height = self.height - bl_pos = 0 - bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) - bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell - return bl_wire - - def graph_exclude_bits(self, targ_row=None, targ_col=None): - """ - Excludes bits in column from being added to graph except target - """ - self.bitcell_array.graph_exclude_bits(targ_row, targ_col) - - def graph_exclude_replica_col_bits(self): - """ - Exclude all replica/dummy cells in the replica columns except the replica bit. - """ - - for port in self.left_rbl + self.right_rbl: - self.replica_columns[port].exclude_all_but_replica() - - def get_cell_name(self, inst_name, row, col): - """ - Gets the spice name of the target bitcell. - """ - return self.bitcell_array.get_cell_name(inst_name + "{}x".format(OPTS.hier_seperator) + self.bitcell_array_inst.name, row, col) - - def clear_exclude_bits(self): - """ - Clears the bit exclusions - """ - self.bitcell_array.init_graph_params() diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index 4c37fb11..90c169c7 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -19,21 +19,9 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): Assumes bit-lines and word lines are connected by abutment. """ def __init__(self, rows, cols, column_offset=0, name=""): - # Don't call the regular bitcell_array constructor since we don't want its constructor, just - # some of it's useful member functions - sky130_bitcell_base_array.__init__(self, rows=rows, cols=cols, column_offset=column_offset, name=name) - if self.row_size % 2 == 0: - debug.error("Invalid number of rows {}. number of rows (excluding dummy rows) must be odd to connect to col ends".format(self.row_size), -1) - debug.info(1, "Creating {0} {1} x {2}".format(self.name, self.row_size, self.column_size)) - self.add_comment("rows: {0} cols: {1}".format(self.row_size, self.column_size)) - - # This will create a default set of bitline/wordline names - self.create_all_bitline_names() - self.create_all_wordline_names() - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - #self.add_supply_pins() + if rows % 2 == 0: + debug.error("Invalid number of rows {}. number of rows (excluding dummy rows) must be odd to connect to col ends".format(rows), -1) + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) def add_modules(self): """ Add the modules used in this design """ @@ -49,19 +37,22 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} - bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \ - + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False, mirror='MX')]\ - + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \ - + [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')] + + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='MX')] - bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True)] \ - + [geometry.instance("11_strapa", mod=self.strapa, is_bitcell=False)] \ - + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False)] + bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.cella, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("11_strap_p", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("12_opt1a", mod=self.cella, is_bitcell=True)] \ + + [geometry.instance("13_strapa", mod=self.strapa, is_bitcell=False)] bit_block = [] pattern.append_row_to_block(bit_block, bit_row_opt1) pattern.append_row_to_block(bit_block, bit_row_opt1a) - self.pattern = pattern(self, "bitcell_array", bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.row_size/2), name_template="bit_r{0}_c{1}") + for row in bit_block: + row = pattern.rotate_list(row, self.column_offset * 2) + self.pattern = pattern(self, "bitcell_array", 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="bit_r{0}_c{1}") self.pattern.connect_array() diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 68814d65..7ad024ab 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -86,33 +86,34 @@ class sky130_bitcell_base_array(bitcell_base_array): def route_supplies(self): # Copy a vdd/gnd layout pin from every cell - print("routing power") + super().route_supplies() for inst in self.insts: if "VPWR" in inst.mod.pins: self.copy_layout_pin(inst, "VPWR", "vdd") if "VGND" in inst.mod.pins: self.copy_layout_pin(inst, "VGND", "gnd") - - for col in range(self.column_size): - inst = self.cell_inst[0,col] - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - - try: - from openram.tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - except: - pin = inst.get_pin("vnb") - self.add_label("vdd", pin.layer, pin.center()) + + if hasattr(self, 'cell_inst'): + for col in range(self.column_size): + inst = self.cell_inst[0,col] + pin = inst.get_pin("vpb") + self.objs.append(geometry.rectangle(layer["nwell"], + pin.ll(), + pin.width(), + pin.height())) + self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) + + try: + from openram.tech import layer_override + if layer_override['VNB']: + pin = inst.get_pin("vnb") + self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) + self.objs.append(geometry.rectangle(layer["pwellp"], + pin.ll(), + pin.width(), + pin.height())) + except: + pin = inst.get_pin("vnb") + self.add_label("vdd", pin.layer, pin.center()) diff --git a/technology/sky130/custom/sky130_dummy_array.py b/technology/sky130/custom/sky130_dummy_array.py index 3e1c5805..8fd3e00f 100644 --- a/technology/sky130/custom/sky130_dummy_array.py +++ b/technology/sky130/custom/sky130_dummy_array.py @@ -10,186 +10,63 @@ from openram.sram_factory import factory from openram.tech import layer from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array +from openram.modules import dummy_array +from openram.modules import pattern +from math import ceil -class sky130_dummy_array(sky130_bitcell_base_array): +class sky130_dummy_array(dummy_array, sky130_bitcell_base_array): """ Generate a dummy row/column for the replica array. """ 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.location = location + self.row_offset = row_offset self.mirror = mirror - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - """ Create and connect the netlist """ - # This will create a default set of bitline/wordline names - self.create_all_bitline_names() - self.create_all_wordline_names() - - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - self.place_array("dummy_r{0}_c{1}", self.mirror) - - self.add_layout_pins() - self.add_supply_pins() - self.add_boundary() - - self.DRC_LVS() + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) def add_modules(self): """ Add the modules used in this design """ self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell, version="opt1") - self.dummy_cell2 = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a") + self.dummy_cella = factory.create(module_type=OPTS.dummy_bitcell, version="opt1a") self.strap = factory.create(module_type="internal", version="wlstrap") - self.strap2 = factory.create(module_type="internal", version="wlstrap_p") - self.strap3 = factory.create(module_type="internal", version="wlstrapa") - self.strap4 = factory.create(module_type="internal", version="wlstrapa_p") + self.strap_p = factory.create(module_type="internal", version="wlstrap_p") + self.strapa = factory.create(module_type="internal", version="wlstrapa") + self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p") self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - self.array_layout = [] - alternate_bitcell = (self.row_size + 1) % 2 - for row in range(0, self.row_size): + # this code needs to use connect_array_raw() to make dummy columns correctly, but single port shouldn't need these since there are dedicated cap cells + self.all_inst={} + self.cell_inst={} + + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("01_strap_p", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("03_strap", mod=self.strap, is_bitcell=False, mirror='MX')] + + bit_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cella, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("11_strap_p", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("12_opt1a", mod=self.dummy_cella, is_bitcell=True)] \ + + [geometry.instance("13_strapaa", mod=self.strapa, is_bitcell=False)] - row_layout = [] - - alternate_strap = (self.row_size + 1) % 2 - for col in range(0, self.column_size): - if alternate_bitcell == 1: - row_layout.append(self.dummy_cell) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.dummy_cell) - else: - row_layout.append(self.dummy_cell2) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.dummy_cell2) - self.connect_inst(self.get_bitcell_pins(row, col)) - if col != self.column_size - 1: - if alternate_strap: - if col % 2: - name="row_{}_col_{}_wlstrap_p".format(row, col) - row_layout.append(self.strap4) - self.add_inst(name=name, - mod=self.strap4) - else: - name="row_{}_col_{}_wlstrapa_p".format(row, col) - row_layout.append(self.strap2) - self.add_inst(name=name, - mod=self.strap2) - alternate_strap = 0 - else: - if col % 2: - name="row_{}_col_{}_wlstrap".format(row, col) - row_layout.append(self.strap) - self.add_inst(name=name, - mod=self.strap) - else: - name="row_{}_col_{}_wlstrapa".format(row, col) - row_layout.append(self.strap3) - self.add_inst(name=name, - mod=self.strap3) - alternate_strap = 1 - - self.connect_inst(self.get_strap_pins(row, col, name)) - if alternate_bitcell == 0: - alternate_bitcell = 1 + + bit_block = [] + if(self.row_offset % 2 == 0): + next_row = 0 + else: + next_row = 1 + + for i in range(self.row_size): + if next_row == 0: + pattern.append_row_to_block(bit_block, bit_row_opt1) + next_row = 1 else: - alternate_bitcell = 0 - self.array_layout.append(row_layout) + pattern.append_row_to_block(bit_block, bit_row_opt1a) + next_row = 0 - def add_pins(self): - # bitline pins are not added because they are floating - for bl in range(self.column_size): - self.add_pin("bl_0_{}".format(bl)) - self.add_pin("br_0_{}".format(bl)) - for wl_name in self.get_wordline_names(): - self.add_pin(wl_name, "INPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - #self.add_pin("vpb", "BIAS") - #Sself.add_pin("vnb", "BIAS") + for row in bit_block: + row = pattern.rotate_list(row, self.column_offset * 2) - def add_layout_pins(self): - """ Add the layout pins """ - bitline_names = self.cell.get_all_bitline_names() - for col in range(self.column_size): - for port in self.all_ports: - bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port]) - text = "bl_{0}_{1}".format(port, col) - self.add_layout_pin(text=text, - layer=bl_pin.layer, - offset=bl_pin.ll().scale(1, 0), - width=bl_pin.width(), - height=self.height) - br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1]) - text = "br_{0}_{1}".format(port, col) - self.add_layout_pin(text=text, - layer=br_pin.layer, - offset=br_pin.ll().scale(1, 0), - width=br_pin.width(), - height=self.height) - # self.add_rect(layer=bl_pin.layer, - # offset=bl_pin.ll().scale(1, 0), - # width=bl_pin.width(), - # height=self.height) - # self.add_rect(layer=br_pin.layer, - # offset=br_pin.ll().scale(1, 0), - # width=br_pin.width(), - # height=self.height) - - wl_names = self.cell.get_all_wl_names() - for row in range(self.row_size): - for port in self.all_ports: - wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port]) - self.add_layout_pin(text="wl_{0}_{1}".format(port, row), - layer=wl_pin.layer, - offset=wl_pin.ll().scale(0, 1), - width=self.width, - height=wl_pin.height()) - - # Copy a vdd/gnd layout pin from every cell - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - for pin_name in ["vdd", "gnd"]: - self.copy_layout_pin(inst, pin_name) - - def add_supply_pins(self): - for row in range(self.row_size): - for col in range(self.column_size): - inst = self.cell_inst[row, col] - if 'VPB' or 'vpb' in self.cell_inst[row, col].mod.pins: - pin = inst.get_pin("vpb") - self.objs.append(geometry.rectangle(layer["nwell"], - pin.ll(), - pin.width(), - pin.height())) - self.objs.append(geometry.label("vdd", layer["nwell"], pin.center())) - - if 'VNB' or 'vnb' in self.cell_inst[row, col].mod.pins: - try: - from openram.tech import layer_override - if layer_override['VNB']: - pin = inst.get_pin("vnb") - self.objs.append(geometry.label("gnd", layer["pwellp"], pin.center())) - self.objs.append(geometry.rectangle(layer["pwellp"], - pin.ll(), - pin.width(), - pin.height())) - except: - pin = inst.get_pin("vnb") - self.add_label("vdd", pin.layer, pin.center()) - - def input_load(self): - # FIXME: This appears to be old code from previous characterization. Needs to be updated. - wl_wire = self.gen_wl_wire() - return wl_wire.return_input_cap() + self.pattern = pattern(self, "bitcell_array", 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="dummy_bit_r{0}_c{1}") + self.pattern.connect_array() diff --git a/technology/sky130/custom/sky130_replica_bitcell_array.py b/technology/sky130/custom/sky130_replica_bitcell_array.py index 33a52492..f6d9b7aa 100644 --- a/technology/sky130/custom/sky130_replica_bitcell_array.py +++ b/technology/sky130/custom/sky130_replica_bitcell_array.py @@ -13,7 +13,7 @@ from openram.tech import drc from openram.tech import array_row_multiple from openram.tech import array_col_multiple from openram import OPTS -from .replica_bitcell_array import replica_bitcell_array +from openram.modules import replica_bitcell_array from .sky130_bitcell_base_array import sky130_bitcell_base_array @@ -27,389 +27,38 @@ class sky130_replica_bitcell_array(replica_bitcell_array, sky130_bitcell_base_ar bitcell (Bl/BR disconnected). """ def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""): - total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports - self.all_ports = list(range(total_ports)) - - self.column_size = cols - self.row_size = rows - - # This is how many RBLs are in all the arrays - if rbl: - self.rbl = rbl - else: - self.rbl=[1, 1 if len(self.all_ports)>1 else 0] - # This specifies which RBL to put on the left or right - # by port number - # This could be an empty list - if left_rbl != None: - self.left_rbl = left_rbl - else: - self.left_rbl = [0] - # This could be an empty list - if right_rbl != None: - self.right_rbl = right_rbl - else: - self.right_rbl=[1] if len(self.all_ports) > 1 else [] - self.rbls = self.left_rbl + self.right_rbl - - if ((self.column_size + self.rbl[0] + self.rbl[1]) % array_col_multiple != 0): - debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.column_size + self.rbl[0] + self.rbl[1], array_col_multiple), -1) - - if ((self.row_size + self.rbl[0] + self.rbl[1]) % array_row_multiple != 0): - debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.row_size + self.rbl[0] + self.rbl[1], array_row_multiple), -15) - - super().__init__(self.row_size, self.column_size, rbl, left_rbl, right_rbl, name) - - def create_layout(self): - # We will need unused wordlines grounded, so we need to know their layer - # and create a space on the left and right for the vias to connect to ground - pin = self.cell.get_pin(self.cell.get_all_wl_names()[0]) - pin_layer = pin.layer - self.unused_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer)) - self.unused_offset = vector(self.unused_pitch, 0) - - # This is a bitcell x bitcell offset to scale - self.bitcell_offset = vector(self.cell.width, self.cell.height) - self.strap_offset = vector(self.replica_col_insts[0].mod.strap1.width, self.replica_col_insts[0].mod.strap1.height) - self.col_end_offset = vector(self.dummy_row_insts[0].mod.colend1.width, self.dummy_row_insts[0].mod.colend1.height) - self.row_end_offset = vector(self.dummy_col_insts[0].mod.rowend1.width, self.dummy_col_insts[0].mod.rowend1.height) - - # Everything is computed with the main array at (self.unused_pitch, 0) to start - self.bitcell_array_inst.place(offset=self.unused_offset) - - self.add_replica_columns() - - self.add_end_caps() - - # Array was at (0, 0) but move everything so it is at the lower left - self.offset_all_coordinates() - - # Add extra width on the left and right for the unused WLs - #self.width = self.dummy_col_insts[0].rx() + self.unused_offset[0] - self.width = self.dummy_col_insts[1].rx() - self.height = self.dummy_col_insts[0].uy() - - self.add_layout_pins() - - self.route_unused_wordlines() - - self.add_boundary() - - self.DRC_LVS() - - def add_pins(self): - super().add_pins() - - def add_replica_columns(self): - """ Add replica columns on left and right of array """ - - # Grow from left to right, toward the array - for bit, port in enumerate(self.left_rbl): - offset = self.bitcell_array_inst.ll() \ - - vector(0, self.col_cap_bottom.height) \ - - vector(0, self.dummy_row.height) \ - - vector(self.replica_columns[0].width, 0) - self.replica_col_insts[bit].place(offset + vector(0, self.replica_col_insts[bit].height), mirror="MX") - - # Grow to the right of the bitcell array, array outward - for bit, port in enumerate(self.right_rbl): - 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) - self.replica_col_insts[self.rbl[0] + bit].place(offset) - - # Replica dummy rows - # Add the dummy rows even if we aren't adding the replica column to this bitcell array - # These grow up, toward the array - for bit in range(self.rbl[0]): - dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2) + self.unused_offset - self.dummy_row_replica_insts[bit].place(offset=dummy_offset, - mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0") - # These grow up, away from the array - for bit in range(self.rbl[1]): - dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul() - self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset, - mirror="MX" if bit % 2 else "R0") - - def add_end_caps(self): - """ Add dummy cells or end caps around the array """ - - dummy_row_offset = self.bitcell_offset.scale(0, self.rbl[1]) + self.bitcell_array_inst.ul() - self.dummy_row_insts[1].place(offset=dummy_row_offset) - - dummy_row_offset = self.bitcell_offset.scale(0, -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) + self.unused_offset - self.dummy_row_insts[0].place(offset=dummy_row_offset + vector(0, self.dummy_row_insts[0].height), mirror="MX") - - # 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(len(self.right_rbl) * (1 + self.strap_offset.x / self.cell.width), -self.rbl[0] - (self.col_end_offset.y / self.cell.height)) - vector(self.replica_col_insts[0].width, 0) + self.unused_offset - self.dummy_col_insts[0].place(offset=dummy_col_offset, mirror="MY") - - # 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(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() - self.dummy_col_insts[1].place(offset=dummy_col_offset) - - def route_unused_wordlines(self): - """ Connect the unused RBL and dummy wordlines to gnd """ - return - # This grounds all the dummy row word lines - for inst in self.dummy_row_insts: - for wl_name in self.col_cap.get_wordline_names(): - self.ground_pin(inst, wl_name) - - # Ground the unused replica wordlines - for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts): - for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()): - if wl_name in self.gnd_wordline_names: - self.ground_pin(inst, pin_name) - - def add_layout_pins(self): - """ Add the layout pins """ - - for row_end in self.dummy_col_insts: - row_end = row_end.mod - for (rba_wl_name, wl_name) in zip(self.get_all_wordline_names(), row_end.get_wordline_names()): - pin = row_end.get_pin(wl_name) - self.add_layout_pin(text=rba_wl_name, - layer=pin.layer, - offset=vector(0,pin.ll().scale(0, 1)[1]), - #width=self.width, - width=pin.width(), - height=pin.height()) - - pin_height = (round_to_grid(drc["minarea_m3"] / round_to_grid(sqrt(drc["minarea_m3"]))) + drc["{0}_to_{0}".format('m3')]) - drc_width = drc["{0}_to_{0}".format('m3')] - - # vdd/gnd are only connected in the perimeter cells - # replica column should only have a vdd/gnd in the dummy cell on top/bottom - supply_insts = self.dummy_row_insts + self.replica_col_insts - - for pin_name in self.supplies: - for supply_inst in supply_insts: - vdd_alternate = 0 - gnd_alternate = 0 - for cell_inst in supply_inst.mod.insts: - inst = cell_inst.mod - for pin in inst.get_pins(pin_name): - if pin.name == 'vdd': - if vdd_alternate: - connection_offset = 0.035 - vdd_alternate = 0 - else: - connection_offset = -0.035 - vdd_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 1 - elif pin.name == 'gnd': - if gnd_alternate: - connection_offset = 0.035 - gnd_alternate = 0 - else: - connection_offset = -0.035 - gnd_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 4 - pin_width = round_to_grid(sqrt(drc["minarea_m3"])) - pin_height = round_to_grid(drc["minarea_m3"] / pin_width) - if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent' or 'corner' in inst.cell_name: - if 'dummy_row' in supply_inst.name and supply_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'dummy_row' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width) - elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'replica_col' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width) - self.add_via_stack_center(from_layer=pin.layer, - to_layer='m2', - offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0)) + + super().__init__(rows, cols, rbl, left_rbl, right_rbl, name) + # total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + # self.all_ports = list(range(total_ports)) + # + # self.column_size = cols + # self.row_size = rows + # + # # This is how many RBLs are in all the arrays + # if rbl: + # self.rbl = rbl + # else: + # self.rbl=[1, 1 if len(self.all_ports)>1 else 0] + # # This specifies which RBL to put on the left or right + # # by port number + # # This could be an empty list + # if left_rbl != None: + # self.left_rbl = left_rbl + # else: + # self.left_rbl = [0] + # # This could be an empty list + # if right_rbl != None: + # self.right_rbl = right_rbl + # else: + # self.right_rbl=[1] if len(self.all_ports) > 1 else [] + # self.rbls = self.left_rbl + self.right_rbl + # + # if ((self.column_size + self.rbl[0] + self.rbl[1]) % array_col_multiple != 0): + # debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.column_size + self.rbl[0] + self.rbl[1], array_col_multiple), -1) + # + # if ((self.row_size + self.rbl[0] + self.rbl[1]) % array_row_multiple != 0): + # debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.row_size + self.rbl[0] + self.rbl[1], array_row_multiple), -15) + # - # add well contacts to perimeter cells - for pin_name in ['vpb', 'vnb']: - for supply_inst in supply_insts: - vnb_alternate = 0 - vpb_alternate = 0 - for cell_inst in supply_inst.mod.insts: - - inst = cell_inst.mod - for pin in inst.get_pins(pin_name): - if pin.name == 'vpb': - if vpb_alternate: - connection_offset = 0.01 - vpb_alternate = 0 - else: - connection_offset = 0.02 - vpb_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 2 - elif pin.name == 'vnb': - if vnb_alternate: - connection_offset = -0.01 - vnb_alternate = 0 - else: - connection_offset = -0.02 - vnb_alternate = 1 - connection_width = drc["minwidth_{}".format('m1')] - track_offset = 3 - if inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_p_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colend_cent' or inst.cell_name == 'sky130_fd_bd_sram__sram_sp_colenda_cent': - if 'dummy_row' in supply_inst.name and supply_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'dummy_row' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, self.height), connection_width) - elif 'replica_col' in supply_inst.name and cell_inst.mirror == 'MX': - pin_center = vector(pin.center()[0], -1 * track_offset* (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset, 0), connection_width) - elif 'replica_col' in supply_inst.name: - pin_center = vector(pin.center()[0],inst.height + 1 * track_offset * (pin_height + drc_width*2)) - self.add_segment_center(pin.layer, pin_center+supply_inst.ll()+cell_inst.ll()+vector(connection_offset,0), vector((pin_center+supply_inst.ll()+cell_inst.ll())[0] + connection_offset,self.height), connection_width) - self.add_via_stack_center(from_layer=pin.layer, - to_layer='m2', - offset=pin_center+supply_inst.ll()+cell_inst.ll() + vector(connection_offset,0)) - - min_area = drc["minarea_{}".format('m3')] - for track,supply, offset in zip(range(1,5),['vdd','vdd','gnd','gnd'],[min_area * 6,min_area * 6, 0, 0]): - y_offset = track * (pin_height + drc_width*2) - self.add_segment_center('m2', vector(0,-y_offset), vector(self.width, -y_offset), drc["minwidth_{}".format('m2')]) - self.add_segment_center('m2', vector(0,self.height + y_offset), vector(self.width, self.height + y_offset), drc["minwidth_{}".format('m2')]) - self.add_power_pin(name=supply, - loc=vector(round_to_grid(sqrt(min_area))/2 + offset, -y_offset), - start_layer='m2') - self.add_power_pin(name=supply, - loc=vector(round_to_grid(sqrt(min_area))/2 + offset, self.height + y_offset), - start_layer='m2') - self.add_power_pin(name=supply, - loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, -y_offset), - start_layer='m2') - self.add_power_pin(name=supply, - loc=vector(self.width - round_to_grid(sqrt(min_area))/2 - offset, self.height + y_offset), - start_layer='m2') - - self.offset_all_coordinates() - self.height = self.height + self.dummy_col_insts[0].lr().y * 2 - - for pin_name in self.all_bitline_names: - pin_list = self.bitcell_array_inst.get_pins(pin_name) - for pin in pin_list: - if 'bl' in pin.name: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - elif 'br' in pin_name: - self.add_layout_pin(text=pin_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0) + vector(0,pin_height + drc_width*2), - width=pin.width(), - height=self.height - 2 *(pin_height + drc_width*2)) - # Replica bitlines - if len(self.rbls) > 0: - for (names, inst) in zip(self.rbl_bitline_names, self.replica_col_insts): - pin_names = self.replica_columns[self.rbls[0]].all_bitline_names - mirror = self.replica_col_insts[0].mirror - for (bl_name, pin_name) in zip(names, pin_names): - pin = inst.get_pin(pin_name) - if 'rbl_bl' in bl_name: - # if mirror != "MY": - # bl_name = bl_name.replace("rbl_bl","rbl_br") - self.add_layout_pin(text=bl_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0), - width=pin.width(), - height=self.height) - elif 'rbl_br' in bl_name: - # if mirror != "MY": - # bl_name = bl_name.replace("rbl_br","rbl_bl") - self.add_layout_pin(text=bl_name, - layer=pin.layer, - offset=pin.ll().scale(1, 0) + vector(0,(pin_height + drc_width*2)), - width=pin.width(), - height=self.height - 2 *(pin_height + drc_width*2)) - return - - def add_wordline_pins(self): - - # Wordlines to ground - self.gnd_wordline_names = [] - - for port in self.all_ports: - for bit in self.all_ports: - self.rbl_wordline_names[port].append("rbl_wl_{0}_{1}".format(port, bit)) - if bit != port: - self.gnd_wordline_names.append("rbl_wl_{0}_{1}".format(port, bit)) - - self.all_rbl_wordline_names = [x for sl in self.rbl_wordline_names for x in sl] - - self.wordline_names = self.bitcell_array.wordline_names - self.all_wordline_names = self.bitcell_array.all_wordline_names - - # All wordlines including dummy and RBL - self.replica_array_wordline_names = [] - #self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - for bit in range(self.rbl[0]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[bit]]) - self.replica_array_wordline_names.extend(self.all_wordline_names) - for bit in range(self.rbl[1]): - self.replica_array_wordline_names.extend([x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[self.rbl[0] + bit]]) - #self.replica_array_wordline_names.extend(["gnd"] * len(self.col_cap_top.get_wordline_names())) - - for port in range(self.rbl[0]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - self.add_pin_list(self.all_wordline_names, "INPUT") - for port in range(self.rbl[0], self.rbl[0] + self.rbl[1]): - self.add_pin(self.rbl_wordline_names[port][port], "INPUT") - - def create_instances(self): - """ Create the module instances used in this design """ - self.supplies = ["vdd", "gnd"] - - # Used for names/dimensions only - # self.cell = factory.create(module_type=OPTS.bitcell) - - # Main array - self.bitcell_array_inst=self.add_inst(name="bitcell_array", - mod=self.bitcell_array) - self.connect_inst(self.all_bitline_names + self.all_wordline_names + self.supplies) - # Replica columns - self.replica_col_insts = [] - for port in self.all_ports: - if port in self.rbls: - self.replica_col_insts.append(self.add_inst(name="replica_col_{}".format(port), - mod=self.replica_columns[port])) - self.connect_inst(self.rbl_bitline_names[port] + self.replica_array_wordline_names + self.supplies + ["gnd"] + ["gnd"]) - else: - self.replica_col_insts.append(None) - - # Dummy rows under the bitcell array (connected with with the replica cell wl) - self.dummy_row_replica_insts = [] - # Note, this is the number of left and right even if we aren't adding the columns to this bitcell array! - for port in self.all_ports: - self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port), - mod=self.dummy_row)) - self.connect_inst(self.all_bitline_names + [x if x not in self.gnd_wordline_names else "gnd" for x in self.rbl_wordline_names[port]] + self.supplies) - - # Top/bottom dummy rows or col caps - self.dummy_row_insts = [] - self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot", - mod=self.col_cap_bottom)) - self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"]) - self.dummy_row_insts.append(self.add_inst(name="dummy_row_top", - mod=self.col_cap_top)) - self.connect_inst(self.all_bitline_names + self.supplies + ["gnd"]) - - # Left/right Dummy columns - self.dummy_col_insts = [] - self.dummy_col_insts.append(self.add_inst(name="dummy_col_left", - mod=self.row_cap_left)) - self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies) - self.dummy_col_insts.append(self.add_inst(name="dummy_col_right", - mod=self.row_cap_right)) - self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + ["gnd"] + self.replica_array_wordline_names + ["gnd"] + self.supplies) diff --git a/technology/sky130/custom/sky130_replica_column.py b/technology/sky130/custom/sky130_replica_column.py index 64486e14..6a749b2d 100644 --- a/technology/sky130/custom/sky130_replica_column.py +++ b/technology/sky130/custom/sky130_replica_column.py @@ -12,8 +12,8 @@ from openram.tech import layer from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array from openram.modules import pattern - -class sky130_replica_column(sky130_bitcell_base_array): +from openram.modules import replica_column +class sky130_replica_column(replica_column, sky130_bitcell_base_array): """ Generate a replica bitline column for the replica array. Rows is the total number of rows i the main array. @@ -23,64 +23,7 @@ class sky130_replica_column(sky130_bitcell_base_array): """ def __init__(self, name, rows, rbl, replica_bit, column_offset=0): - # 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] - # 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) - - 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 - self.column_offset = column_offset - - # if self.rows % 2 == 0: - # debug.error("Invalid number of rows {}. Number of rows must be even to connect to col ends".format(self.rows), -1) - # if self.column_offset % 2 == 0: - # debug.error("Invalid column_offset {}. Column offset must be odd to connect to col ends".format(self.rows), -1) - # 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, - # "Replica bit cannot be in the regular array.") - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def create_netlist(self): - self.add_modules() - self.add_pins() - self.create_instances() - - def create_layout(self): - self.place_array() - - self.add_layout_pins() - self.route_supplies() - self.add_boundary() - self.DRC_LVS() - - def add_pins(self): - - self.create_all_bitline_names() - #self.create_all_wordline_names(self.row_size+2) - # +2 to add fake wl pins for colends - 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") - - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - #self.add_pin("top_gate", "INPUT") - #self.add_pin("bot_gate", "INPUT") + super().__init__(name=name, rows=rows, rbl=rbl, replica_bit=replica_bit, column_offset=column_offset) def add_modules(self): self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1") @@ -88,51 +31,65 @@ class sky130_replica_column(sky130_bitcell_base_array): self.replica_cell2 = factory.create(module_type="replica_bitcell_1port", version="opt1a") self.dummy_cell = factory.create(module_type="dummy_bitcell_1port", version="opt1") - self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1") - - self.strap = factory.create(module_type="internal", version="wlstrap_p") - self.strap2 = factory.create(module_type="internal", version="wlstrapa_p") + self.dummy_cell2 = factory.create(module_type="dummy_bitcell_1port", version="opt1a") + self.strap = factory.create(module_type="internal", version="wlstrap") + self.strap_p = factory.create(module_type="internal", version="wlstrap_p") + self.strapa = factory.create(module_type="internal", version="wlstrapa") + self.strapa_p = factory.create(module_type="internal", version="wlstrapa_p") def create_instances(self): """ Create the module instances used in this design """ self.all_inst={} self.cell_inst={} - replica_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \ - + [geometry.instance("01_strap1", mod=self.strap, is_bitcell=False, mirror='MX')] - - replica_row_opt1a = [geometry.instance("10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] - replica_dummy_row_opt1 = [geometry.instance("00_rep_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ - + [geometry.instance("01_rep_strap", mod=self.strap, is_bitcell=False, mirror='MX')] + replica_row_opt1 = [geometry.instance("rep_00_opt1", mod=self.replica_cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("rep_01_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("rep_02_opt1", mod=self.replica_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("rep_03_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')] - replica_dummy_row_opt1a = [geometry.instance("10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \ - + [geometry.instance("11_strapa", mod=self.strap2, is_bitcell=False)] + replica_row_opt1a = [geometry.instance("rep_10_opt1a", mod=self.replica_cell2, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("rep_11_strapa", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("rep_12_opt1a", mod=self.replica_cell2, is_bitcell=True)] \ + + [geometry.instance("rep_13_strapa_p", mod=self.strapa, is_bitcell=False)] + + dummy_row_opt1 = [geometry.instance("dummy_00_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='XY')] \ + + [geometry.instance("dummy_01_strap", mod=self.strap_p, is_bitcell=False, mirror='MX')]\ + + [geometry.instance("dummy_02_opt1", mod=self.dummy_cell, is_bitcell=True, mirror='MX')] \ + + [geometry.instance("dummy_03_strap_p", mod=self.strap, is_bitcell=False, mirror='MX')] + + dummy_row_opt1a = [geometry.instance("dummy_10_opt1a", mod=self.dummy_cell2, is_bitcell=True, mirror='MY')] \ + + [geometry.instance("dummy_11_strapa", mod=self.strap_p, is_bitcell=False)] \ + + [geometry.instance("dummy_12_opt1a", mod=self.dummy_cell2, is_bitcell=True)] \ + + [geometry.instance("dummy_13_strapa_p", mod=self.strapa, is_bitcell=False)] bit_block = [] + if self.column_offset % 2: + replica_row_opt1 = replica_row_opt1[0:2] + replica_row_opt1a = replica_row_opt1a[0:2] + dummy_row_opt1 = dummy_row_opt1[0:2] + dummy_row_opt1a = dummy_row_opt1a[0:2] + else: + replica_row_opt1 = replica_row_opt1[2:4] + replica_row_opt1a = replica_row_opt1a[2:4] + dummy_row_opt1 = dummy_row_opt1[2:4] + dummy_row_opt1a = dummy_row_opt1a[2:4] + current_row = self.row_start for row in range(self.total_size): # Regular array cells are replica cells # 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: + if current_row % 2 == 0: pattern.append_row_to_block(bit_block, replica_row_opt1) else: pattern.append_row_to_block(bit_block, replica_row_opt1a) else: - if current_row %2: - pattern.append_row_to_block(bit_block, replica_dummy_row_opt1) + if current_row % 2 == 0: + pattern.append_row_to_block(bit_block, dummy_row_opt1) else: - pattern.append_row_to_block(bit_block, replica_dummy_row_opt1a) + pattern.append_row_to_block(bit_block, dummy_row_opt1a) current_row += 1 - self.pattern = pattern(self, "replica_column", bit_block, num_rows=self.total_size, num_cols=len(replica_row_opt1a), name_template="rbc_r{0}_c{1}", ) + self.pattern = pattern(self, "replica_column", bit_block, num_rows=self.total_size, num_cols=len(replica_row_opt1), name_template="rbc_r{0}_c{1}") self.pattern.connect_array_raw() - def exclude_all_but_replica(self): - """ - Excludes all bits except the replica cell (self.replica_bit). - """ - for row, cell in self.cell_inst.items(): - if row != self.replica_bit: - self.graph_inst_exclude.add(cell)