From e1967dc548d82a79161918e26dcd0d195e8d4a61 Mon Sep 17 00:00:00 2001 From: mrg Date: Thu, 23 Jul 2020 14:43:14 -0700 Subject: [PATCH] Draft local and global arrays. Ensure rows before cols in usage. --- compiler/modules/bitcell_array.py | 8 +- compiler/modules/bitcell_base_array.py | 4 +- compiler/modules/col_cap_array.py | 4 +- compiler/modules/dummy_array.py | 6 +- compiler/modules/global_bitcell_array.py | 66 +++++++++ compiler/modules/local_bitcell_array.py | 66 +++++++++ compiler/modules/replica_bitcell_array.py | 2 +- compiler/modules/replica_column.py | 14 +- compiler/modules/row_cap_array.py | 4 +- compiler/modules/wordline_buffer_array.py | 137 ++++++++++++++++++ compiler/modules/wordline_driver_array.py | 1 + .../tests/08_wordline_buffer_array_test.py | 37 +++++ 12 files changed, 328 insertions(+), 21 deletions(-) create mode 100644 compiler/modules/global_bitcell_array.py create mode 100644 compiler/modules/local_bitcell_array.py create mode 100644 compiler/modules/wordline_buffer_array.py create mode 100755 compiler/tests/08_wordline_buffer_array_test.py diff --git a/compiler/modules/bitcell_array.py b/compiler/modules/bitcell_array.py index 97b681e9..ce72b137 100644 --- a/compiler/modules/bitcell_array.py +++ b/compiler/modules/bitcell_array.py @@ -17,8 +17,8 @@ class bitcell_array(bitcell_base_array): 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) + def __init__(self, rows, cols, column_offset=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.create_netlist() if not OPTS.netlist_only: @@ -57,8 +57,8 @@ class bitcell_array(bitcell_base_array): 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)) - + self.connect_inst(self.get_bitcell_pins(row, col)) + def analytical_power(self, corner, load): """Power of Bitcell array and bitline in nW.""" diff --git a/compiler/modules/bitcell_base_array.py b/compiler/modules/bitcell_base_array.py index 7d241b4d..793cfc34 100644 --- a/compiler/modules/bitcell_base_array.py +++ b/compiler/modules/bitcell_base_array.py @@ -14,7 +14,7 @@ class bitcell_base_array(design.design): """ Abstract base class for bitcell-arrays -- bitcell, dummy """ - def __init__(self, cols, rows, name, column_offset): + def __init__(self, name, rows, cols, column_offset): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) @@ -61,7 +61,7 @@ class bitcell_base_array(design.design): self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - def get_bitcell_pins(self, col, row): + 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 """ diff --git a/compiler/modules/col_cap_array.py b/compiler/modules/col_cap_array.py index ee9302d8..d9fe78b5 100644 --- a/compiler/modules/col_cap_array.py +++ b/compiler/modules/col_cap_array.py @@ -50,9 +50,9 @@ class col_cap_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(row, col)) - def get_bitcell_pins(self, col, row): + 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 diff --git a/compiler/modules/dummy_array.py b/compiler/modules/dummy_array.py index f4b240da..9004994b 100644 --- a/compiler/modules/dummy_array.py +++ b/compiler/modules/dummy_array.py @@ -12,8 +12,8 @@ class dummy_array(bitcell_base_array): """ Generate a dummy row/column for the replica array. """ - def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): - super().__init__(cols, rows, name, column_offset) + def __init__(self, rows, cols, column_offset=0, mirror=0, name=""): + super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name) self.mirror = mirror self.create_netlist() @@ -51,7 +51,7 @@ class dummy_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(row, col)) def input_load(self): wl_wire = self.gen_wl_wire() diff --git a/compiler/modules/global_bitcell_array.py b/compiler/modules/global_bitcell_array.py new file mode 100644 index 00000000..728f170f --- /dev/null +++ b/compiler/modules/global_bitcell_array.py @@ -0,0 +1,66 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 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 bitcell_base_array import bitcell_base_array +from tech import drc, spice +from globals import OPTS +from sram_factory import factory +import debug + +class global_bitcell_array(bitcell_base_array): + """ + Creates a global bitcell array with a number + of local arrays of a sizes given by a tuple in the list. + """ + def __init__(self, sizes, name=""): + # Each bank will have the same number of rows + self.rows = sizes[0][0] + for (r, c) in sizes: + debug.check(r[0] == self.rows, "Cannot have non-uniform number of rows in global array.") + # The total of all columns will be the number of columns + self.cols = sum(x[1] for x in sizes) + self.sizes = sizes + super().__init__(rows=self.rows, cols=self.cols, name=name) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + """ Create and connect the netlist """ + self.add_modules() + self.add_pins() + self.create_instances() + + def create_layout(self): + + self.place() + + self.add_layout_pins() + + self.add_boundary() + + self.DRC_LVS() + + def add_modules(self): + """ Add the modules used in this design """ + self.local_mods = [] + for (row, col) in self.sizes: + la = factory.create(module_type="local_bitcell_array", rows=row, cols=col) + self.add_mod(la) + self.local_mods.append(la) + + def create_instances(self): + """ Create the module instances used in this design """ + self.local_inst = {} + for i in range(self.sizes): + name = "local_array_{0}".format(i) + self.local_inst.append(self.add_inst(name=name, + mod=self.local_mods[i]) + self.connect_inst(self.get_bitcell_pins(row, col)) + + diff --git a/compiler/modules/local_bitcell_array.py b/compiler/modules/local_bitcell_array.py new file mode 100644 index 00000000..fc3137c6 --- /dev/null +++ b/compiler/modules/local_bitcell_array.py @@ -0,0 +1,66 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 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 bitcell_base_array import bitcell_base_array +from tech import drc, spice +from globals import OPTS +from sram_factory import factory + + +class local_bitcell_array(bitcell_base_array): + """ + A local wordline array is a bitcell array with a wordline driver. + This can either be a single aray on its own if there is no hierarchical WL + or it can be combined into a larger array with hierarchical WL. + """ + def __init__(self, name, rows, cols): + super().__init__(name, rows, cols) + + 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.bit_array = factory.create(module_type="bitcell_array", + rows=self.rows, + cols=self.cols, + column_offset=self.column_offset) + self.add_mod(self.bit_array) + + self.wl_array = factory.create(module_type="wordline_driver_array", + rows=self.rows, + cols=self.cols) + self.add_mod(self.wl_array) + + def create_instances(self): + """ Create the module instances used in this design """ + self.bitcell_inst = self.add_inst(mod=self.bitcell_array) + self.connect_inst(self.get_bitcell_pins(row, col)) + self.wl_inst = self.add_inst(mod=self.wl_array) + self.connect_inst(self.get_bitcell_pins(row, col)) + diff --git a/compiler/modules/replica_bitcell_array.py b/compiler/modules/replica_bitcell_array.py index 0c7e412e..0c08cd56 100644 --- a/compiler/modules/replica_bitcell_array.py +++ b/compiler/modules/replica_bitcell_array.py @@ -21,7 +21,7 @@ class replica_bitcell_array(design.design): Requires a regular bitcell array, replica bitcell, and dummy bitcell (Bl/BR disconnected). """ - def __init__(self, cols, rows, left_rbl, right_rbl, bitcell_ports, name): + def __init__(self, rows, cols, left_rbl, right_rbl, bitcell_ports, name): design.design.__init__(self, name) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index 9613e6fa..c75bc9ee 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -102,22 +102,22 @@ class replica_column(design.design): if (row > self.left_rbl and row < self.total_size - self.right_rbl - 1): self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) elif row==self.replica_bit: self.cell_inst[row]=self.add_inst(name=name, mod=self.replica_cell) - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) elif (row == 0 or row == self.total_size - 1): self.cell_inst[row]=self.add_inst(name=name, mod=self.edge_cell) if end_caps_enabled: - self.connect_inst(self.get_bitcell_pins_col_cap(0, row)) + self.connect_inst(self.get_bitcell_pins_col_cap(row, 0)) else: - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) else: self.cell_inst[row]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(0, row)) + self.connect_inst(self.get_bitcell_pins(row, 0)) def place_instances(self): from tech import cell_properties @@ -191,7 +191,7 @@ class replica_column(design.design): else: self.copy_layout_pin(inst, pin_name) - def get_bitcell_pins(self, col, row): + 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 """ @@ -208,7 +208,7 @@ class replica_column(design.design): return bitcell_pins - def get_bitcell_pins_col_cap(self, col, row): + def get_bitcell_pins_col_cap(self, row, col): """ Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """ diff --git a/compiler/modules/row_cap_array.py b/compiler/modules/row_cap_array.py index f108d86e..bfbd035c 100644 --- a/compiler/modules/row_cap_array.py +++ b/compiler/modules/row_cap_array.py @@ -49,9 +49,9 @@ class row_cap_array(bitcell_base_array): name = "bit_r{0}_c{1}".format(row, col) self.cell_inst[row, col]=self.add_inst(name=name, mod=self.dummy_cell) - self.connect_inst(self.get_bitcell_pins(col, row)) + self.connect_inst(self.get_bitcell_pins(row, col)) - def get_bitcell_pins(self, col, row): + 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 diff --git a/compiler/modules/wordline_buffer_array.py b/compiler/modules/wordline_buffer_array.py new file mode 100644 index 00000000..7dad327d --- /dev/null +++ b/compiler/modules/wordline_buffer_array.py @@ -0,0 +1,137 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 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. +# +import debug +import design +from tech import drc, layer +from vector import vector +from sram_factory import factory +from globals import OPTS + + +class wordline_buffer_array(design.design): + """ + Creates a Wordline Buffer/Inverter array + """ + + def __init__(self, name, rows, cols): + design.design.__init__(self, name) + debug.info(1, "Creating {0}".format(self.name)) + self.add_comment("rows: {0} cols: {1}".format(rows, cols)) + + self.rows = rows + self.cols = cols + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_modules() + self.add_pins() + self.create_drivers() + + def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + self.place_drivers() + self.route_layout() + self.route_vdd_gnd() + self.offset_all_coordinates() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + # inputs to wordline_driver. + for i in range(self.rows): + self.add_pin("in_{0}".format(i), "INPUT") + # Outputs from wordline_driver. + for i in range(self.rows): + self.add_pin("wl_{0}".format(i), "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + self.wl_driver = factory.create(module_type="inv_dec", + size=self.cols) + self.add_mod(self.wl_driver) + + def route_vdd_gnd(self): + """ + Add a pin for each row of vdd/gnd which + are must-connects next level up. + """ + if OPTS.tech_name == "sky130": + for name in ["vdd", "gnd"]: + supply_pins = self.wld_inst[0].get_pins(name) + for pin in supply_pins: + self.add_layout_pin_segment_center(text=name, + layer=pin.layer, + start=pin.bc(), + end=vector(pin.cx(), self.height)) + else: + # Find the x offsets for where the vias/pins should be placed + xoffset_list = [self.wld_inst[0].rx()] + for num in range(self.rows): + # this will result in duplicate polygons for rails, but who cares + + # use the inverter offset even though it will be the and's too + (gate_offset, y_dir) = self.get_gate_offset(0, + self.wl_driver.height, + num) + # Route both supplies + for name in ["vdd", "gnd"]: + supply_pin = self.wld_inst[num].get_pin(name) + + # Add pins in two locations + for xoffset in xoffset_list: + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name, pin_pos) + + def create_drivers(self): + self.wld_inst = [] + for row in range(self.rows): + self.wld_inst.append(self.add_inst(name="wld{0}".format(row), + mod=self.wl_driver)) + self.connect_inst(["in_{0}".format(row), + "wl_{0}".format(row), + "vdd", "gnd"]) + + def place_drivers(self): + + for row in range(self.rows): + if (row % 2): + y_offset = self.wl_driver.height * (row + 1) + inst_mirror = "MX" + else: + y_offset = self.wl_driver.height * row + inst_mirror = "R0" + + offset = [0, y_offset] + + self.wld_inst[row].place(offset=offset, + mirror=inst_mirror) + + self.width = self.wl_driver.width + self.height = self.wl_driver.height * self.rows + + def route_layout(self): + """ Route all of the signals """ + + for row in range(self.rows): + inst = self.wld_inst[row] + + self.copy_layout_pin(inst, "A", "in_{0}".format(row)) + + # output each WL on the right + wl_offset = inst.get_pin("Z").rc() + self.add_layout_pin_segment_center(text="wl_{0}".format(row), + layer=self.route_layer, + start=wl_offset, + end=wl_offset - vector(self.m1_width, 0)) diff --git a/compiler/modules/wordline_driver_array.py b/compiler/modules/wordline_driver_array.py index e8a3c110..f5f8e639 100644 --- a/compiler/modules/wordline_driver_array.py +++ b/compiler/modules/wordline_driver_array.py @@ -12,6 +12,7 @@ from vector import vector from sram_factory import factory from globals import OPTS + class wordline_driver_array(design.design): """ Creates a Wordline Driver diff --git a/compiler/tests/08_wordline_buffer_array_test.py b/compiler/tests/08_wordline_buffer_array_test.py new file mode 100755 index 00000000..a753a21a --- /dev/null +++ b/compiler/tests/08_wordline_buffer_array_test.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 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. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class wordline_buffer_array_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check wordline driver for single port + debug.info(2, "Checking driver") + tx = factory.create(module_type="wordline_buffer_array", rows=8, cols=32) + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner())