diff --git a/compiler/modules/__init__.py b/compiler/modules/__init__.py index 2f7d257a..df7e2a81 100755 --- a/compiler/modules/__init__.py +++ b/compiler/modules/__init__.py @@ -41,7 +41,6 @@ from .local_bitcell_array import * from .nand2_dec import * from .nand3_dec import * from .nand4_dec import * -from .orig_bitcell_array import * from .pand2 import * from .pand3 import * from .pand4 import * diff --git a/compiler/modules/orig_bitcell_array.py b/compiler/modules/orig_bitcell_array.py deleted file mode 100644 index 55ed5d3b..00000000 --- a/compiler/modules/orig_bitcell_array.py +++ /dev/null @@ -1,116 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from openram.sram_factory import factory -from openram.tech import drc, spice -from openram import OPTS -from .bitcell_base_array import bitcell_base_array - - -class bitcell_array(bitcell_base_array): - """ - Creates a rows x cols array of memory cells. Assumes bit-lines - and word line is connected by abutment. - Connects the word lines and bit lines. - """ - def __init__(self, cols, rows, name, column_offset=0): - super().__init__(cols, rows, name, column_offset) - - 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 create_layout(self): - - self.place_array("bit_r{0}_c{1}") - - self.add_layout_pins() - - self.add_boundary() - - self.DRC_LVS() - - def add_modules(self): - """ Add the modules used in this design """ - 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(self.row_size): - name = "bit_r{0}_c{1}".format(row, col) - self.cell_inst[row, col]=self.add_inst(name=name, - mod=self.cell) - self.connect_inst(self.get_bitcell_pins(col, row)) - - 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_wl_wire(self): - if OPTS.netlist_only: - width = 0 - else: - width = self.width - wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1")) - wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell - return wl_wire - - 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 get_wordline_cin(self): - """Get the relative input capacitance from the wordline connections in all the bitcell""" - # A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns - bitcell_wl_cin = self.cell.get_wl_cin() - total_cin = bitcell_wl_cin * self.column_size - return total_cin - - def graph_exclude_bits(self, targ_row, targ_col): - """Excludes bits in column from being added to graph except target""" - # Function is not robust with column mux configurations - for row in range(self.row_size): - for col in range(self.column_size): - if row == targ_row and col == targ_col: - continue - self.graph_inst_exclude.add(self.cell_inst[row, col]) - - def get_cell_name(self, inst_name, row, col): - """Gets the spice name of the target bitcell.""" - return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col] diff --git a/compiler/modules/pattern.py b/compiler/modules/pattern.py index feb04927..af9d2e52 100644 --- a/compiler/modules/pattern.py +++ b/compiler/modules/pattern.py @@ -9,7 +9,9 @@ from typing import List from typing import Optional from openram.base import design from openram.globals import OPTS -from math import ceil +from math import ceil, floor +from copy import deepcopy + class pattern(): """ This class is used to desribe the internals of a bitcell array. It describes @@ -68,6 +70,8 @@ class pattern(): self.initial_y_block = initial_y_block self.final_x_block = final_x_block self.final_y_block = final_y_block + self.bits_per_row = ceil(self.num_rows/self.num_cores_x) + self.bits_per_col = ceil(self.num_cols/self.num_cores_y) if not OPTS.netlist_only: self.verify_interblock_dimensions() @@ -113,23 +117,39 @@ class pattern(): def connect_block(self, block: block, col: int, row: int): 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.bit_cols) <= self.num_rows): inst = block[dr][dc] if(len(self.bit_rows) <= col + dc): self.bit_rows.append(0) if(len(self.bit_cols) <= row + dr): self.bit_cols.append(0) - if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): + if(row_done or self.bit_cols[row+dr] >= self.num_cols): + row_done = True + continue + if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)): + print(row+dr, col+dc) if(inst.is_bitcell): + #x_bit = sum(bit > 0 for bit in self.bit_rows) + #y_bit = sum(bit > 0 for bit in self.bit_cols) + #print(x_bit, y_bit) + self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) + self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] + self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col+dc], self.bit_cols[row+dr])) self.bit_rows[col+dc] += 1 self.bit_cols[row+dr] += 1 - self.parent_design.cell_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) - self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(row+dr, col+dc)) + + else: + self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc)) + self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col+dc], self.bit_cols[row+dr])) + else: + row_done = True def connect_array(self) -> None: self.bit_rows = [] self.bit_cols = [] + #debug_array = [[None]*12 for _ in range(6)] row = 0 col = 0 for i in range(self.num_cores_y): @@ -138,7 +158,7 @@ class pattern(): col += len(self.core_block[0]) col = 0 row += len(self.core_block) - + def place_inst(self, inst, offset) -> None: x = offset[0] y = offset[1] @@ -148,55 +168,27 @@ class pattern(): x += inst.width inst.place((x, y), inst.mirror, inst.rotate) - - - def place_block(self, block: block, row: int, col: int, place_x: float, place_y: float, bx, by): - x_offset = 0 - y_offset = 0 - bounding_x = bx - bounding_y = by - for dr in range(len(block)): - 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(len(self.bit_rows) <= col + dc): - self.bit_rows.append(0) - if(len(self.bit_cols) <= row + dr): - self.bit_cols.append(0) - if(self.bit_rows[col+dc] < self.num_rows and self.bit_cols[row+dr] < self.num_cols): - inst = self.parent_design.cell_inst[row + dr, col +dc] - if(inst.is_bitcell): - self.bit_rows[col+dc] += 1 - self.bit_cols[row+dr] += 1 - self.place_inst(inst, (place_x + x_offset, place_y + y_offset)) - if(place_x + x_offset + inst.width > bounding_x): - bounding_x = place_x + x_offset + inst.width - if(place_y + y_offset + inst.height > bounding_y): - bounding_y = place_y + y_offset + inst.height - x_offset += inst.width - x_offset = 0 - y_offset += inst.height - return bounding_x, bounding_y - def place_array(self): - self.bit_rows = [] - self.bit_cols = [] - row = 0 - col = 0 - place_x = 0 - place_y = 0 - bounding_x = 0 - bounding_y = 0 - for i in range(self.num_cores_y): - col = 0 - place_x = 0 - for j in range(self.num_cores_x): - self.parent_design.width, self.parent_design.height= self.place_block(self.core_block, row, col, place_x, place_y, bounding_x, bounding_y) - if(self.bit_rows.count(self.num_rows) == self.num_cols and self.bit_cols.count(self.bit_cols) == self.num_rows): - return + (row_max, col_max) = list(self.parent_design.all_inst.keys())[-1] + y = 0 + for row in range(row_max+1): + x = 0 + for col in range(col_max+1): + inst = self.parent_design.all_inst[row, col] + self.place_inst(inst, (x, y)) + x += inst.width + y += inst.height - place_x += self.core_block_width - col += len(self.core_block[0]) - row += len(self.core_block) - place_y += self.core_block_height + self.parent_design.width = max([x.rx() for x in self.parent_design.insts]) + self.parent_design.height = max([x.uy() for x in self.parent_design.insts]) + def append_row_to_block(block, row): + block.append(row) + + def append_block_under_block(base_block, under_block): + base_block = base_block + under_block + + def append_block_right_block(base_block, right_block): + for row in base_block: + row = row + right_block diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py index a92d3653..425803db 100755 --- a/compiler/tests/05_bitcell_array_test.py +++ b/compiler/tests/05_bitcell_array_test.py @@ -31,7 +31,7 @@ class array_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows) + a = factory.create(module_type="bitcell_array", cols=4 + num_spare_cols, rows=4 + num_spare_rows) self.local_check(a) openram.end_openram() diff --git a/technology/sky130/custom/sky130_bitcell_array.py b/technology/sky130/custom/sky130_bitcell_array.py index e5a1704b..4526374e 100644 --- a/technology/sky130/custom/sky130_bitcell_array.py +++ b/technology/sky130/custom/sky130_bitcell_array.py @@ -6,11 +6,12 @@ # from openram import debug -from openram.modules import bitcell_array +from openram.modules import bitcell_array, pattern from openram.sram_factory import factory +from openram.base import geometry from openram import OPTS from .sky130_bitcell_base_array import sky130_bitcell_base_array - +from math import ceil class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ @@ -38,56 +39,29 @@ class sky130_bitcell_array(bitcell_array, sky130_bitcell_base_array): """ Add the modules used in this design """ # Bitcell for port names only self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") - self.cell2 = factory.create(module_type=OPTS.bitcell, version="opt1a") + self.cella = factory.create(module_type=OPTS.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") def create_instances(self): """ Create the module instances used in this design """ - self.cell_inst = {} - self.array_layout = [] - alternate_bitcell = (self.row_size) % 2 - for row in range(0, self.row_size): + self.all_inst={} + self.cell_inst={} + bit_row_opt1 = [geometry.instance("00_opt1", mod=self.cell, is_bitcell=True)] \ + + [geometry.instance("01_strap", mod=self.strap, is_bitcell=False)]\ + + [geometry.instance("02_opt1", mod=self.cell, is_bitcell=True)] \ + + [geometry.instance("03_strap_p", mod=self.strap_p, is_bitcell=False)] + + 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)] \ + + [geometry.instance("13_strapa_p", mod=self.strapa_p, is_bitcell=False)] - row_layout = [] + 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}") + self.pattern.connect_array() - alternate_strap = (self.row_size+1) % 2 - for col in range(0, self.column_size): - if alternate_bitcell == 1: - row_layout.append(self.cell) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.cell) - else: - row_layout.append(self.cell2) - self.cell_inst[row, col]=self.add_inst(name="row_{}_col_{}_bitcell".format(row, col), - mod=self.cell2) - self.connect_inst(self.get_bitcell_pins(row, col)) - if col != self.column_size - 1: - if alternate_strap: - if row % 2: - name="row_{}_col_{}_wlstrapa_p".format(row, col) - row_layout.append(self.strap4) - self.add_inst(name=name, mod=self.strap4) - else: - name="row_{}_col_{}_wlstrap_p".format(row, col) - row_layout.append(self.strap2) - self.add_inst(name=name, mod=self.strap2) - alternate_strap = 0 - else: - if row % 2: - name="row_{}_col_{}_wlstrapa".format(row, col) - row_layout.append(self.strap3) - self.add_inst(name=name.format(row, col), mod=self.strap3) - else: - name="row_{}_col_{}_wlstrap".format(row, col) - row_layout.append(self.strap) - self.add_inst(name=name.format(row, col), mod=self.strap) - alternate_strap = 1 - self.connect_inst(self.get_strap_pins(row, col, name)) - if alternate_bitcell == 0: - alternate_bitcell = 1 - else: - alternate_bitcell = 0 - self.array_layout.append(row_layout) diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 9263f8dd..b1310a6a 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -23,44 +23,6 @@ class sky130_bitcell_base_array(bitcell_base_array): self.cell = factory.create(module_type=OPTS.bitcell, version="opt1") - def place_array(self, name_template, row_offset=0, col_offset=0): - yoffset = 0.0 - - for row in range(0, len(self.array_layout)): - xoffset = 0.0 - for col in range(0, len(self.array_layout[row])): - self.place_inst = self.insts[(col) + (row) * len(self.array_layout[row])] - - if row % 2 == 0: - if col == 0: - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - elif col % 4 == 0: - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - elif col % 4 == 3 : - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - elif col % 4 == 2: - self.place_inst.place(offset=[xoffset + self.cell.width, yoffset + self.cell.height], mirror="XY") - else: - self.place_inst.place(offset=[xoffset, yoffset + self.cell.height], mirror="MX") - else: - if col == 0: - self.place_inst.place(offset=[xoffset, yoffset]) - elif col % 4 == 0: - self.place_inst.place(offset=[xoffset, yoffset]) - elif col % 4 == 3 : - self.place_inst.place(offset=[xoffset, yoffset]) - elif col % 4 == 2: - self.place_inst.place(offset=[xoffset + self.cell.width, yoffset], mirror="MY") - # self.place_inst.place(offset=[xoffset, yoffset]) - else: - self.place_inst.place(offset=[xoffset, yoffset]) - - xoffset += self.place_inst.width - yoffset += self.place_inst.height - - self.width = max([x.rx() for x in self.insts]) - self.height = max([x.uy() for x in self.insts]) - def get_bitcell_pins(self, row, col, swap = False): """ Creates a list of connections in the bitcell, @@ -139,49 +101,28 @@ class sky130_bitcell_base_array(bitcell_base_array): for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(inst, pin_name) - if row == 2: #add only 1 label per col + # if row == 2: #add only 1 label per 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()) - 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 add_bitline_pins(self): - 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) - #if "Y" in self.cell_inst[0, col].mirror: - # text = text.replace("bl", "br") - 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) - #if "Y" in self.cell_inst[0, col].mirror: - # text = text.replace("br", "bl") - self.add_layout_pin(text=text, - layer=br_pin.layer, - offset=br_pin.ll().scale(1, 0), - width=br_pin.width(), - height=self.height) +