From 1fc404060774223b282d953da1e9877a54c73111 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 2 Oct 2020 14:54:12 -0700 Subject: [PATCH 01/12] Add pand4 and pnand4 --- compiler/pgates/pand4.py | 165 ++++++++++++++ compiler/pgates/pnand3.py | 1 - compiler/pgates/pnand4.py | 372 +++++++++++++++++++++++++++++++ compiler/tests/04_pand2_test.py | 4 +- compiler/tests/04_pand3_test.py | 4 +- compiler/tests/04_pand4_test.py | 39 ++++ compiler/tests/04_pnand4_test.py | 42 ++++ 7 files changed, 622 insertions(+), 5 deletions(-) create mode 100644 compiler/pgates/pand4.py create mode 100644 compiler/pgates/pnand4.py create mode 100755 compiler/tests/04_pand4_test.py create mode 100755 compiler/tests/04_pnand4_test.py diff --git a/compiler/pgates/pand4.py b/compiler/pgates/pand4.py new file mode 100644 index 00000000..54d2890e --- /dev/null +++ b/compiler/pgates/pand4.py @@ -0,0 +1,165 @@ +# 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 +from vector import vector +import pgate +from sram_factory import factory + + +class pand4(pgate.pgate): + """ + This is a simple buffer used for driving loads. + """ + def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): + debug.info(1, "Creating pand4 {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.vertical = vertical + self.size = size + + # Creates the netlist and layout + super().__init__(name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + # Shield the cap, but have at least a stage effort of 4 + self.nand = factory.create(module_type="pnand4", + height=self.height, + add_wells=self.vertical) + + # Add the well tap to the inverter because when stacked + # vertically it is sometimes narrower + self.inv = factory.create(module_type="pdriver", + size_list=[self.size], + height=self.height, + add_wells=self.add_wells) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + if self.vertical: + self.height = 2 * self.nand.height + self.width = max(self.nand.width, self.inv.width) + else: + self.width = self.nand.width + self.inv.width + + self.place_insts() + self.add_wires() + self.add_layout_pins() + self.route_supply_rails() + self.add_boundary() + self.DRC_LVS() + + def add_pins(self): + self.add_pin("A", "INPUT") + self.add_pin("B", "INPUT") + self.add_pin("C", "INPUT") + self.add_pin("D", "INPUT") + self.add_pin("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand4_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand4_inv", + mod=self.inv) + self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) + + def place_insts(self): + # Add NAND to the right + self.nand_inst.place(offset=vector(0, 0)) + + if self.vertical: + # Add INV above + self.inv_inst.place(offset=vector(self.inv.width, + 2 * self.nand.height), + mirror="XY") + else: + # Add INV to the right + self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + # Second gnd of the inverter gate + if self.vertical: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + width=self.width) + + if self.vertical: + # Shared between two gates + y_offset = 0.5 * self.height + else: + y_offset = self.height + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, y_offset), + width=self.width) + + def add_wires(self): + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.inv_inst.get_pin("A") + if self.vertical: + route_layer = "m2" + self.add_via_stack_center(offset=z1_pin.center(), + from_layer=z1_pin.layer, + to_layer=route_layer) + self.add_zjog(route_layer, + z1_pin.uc(), + a2_pin.bc(), + "V") + self.add_via_stack_center(offset=a2_pin.center(), + from_layer=a2_pin.layer, + to_layer=route_layer) + else: + route_layer = self.route_layer + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.inv_inst.get_pin("Z") + self.add_layout_pin_rect_center(text="Z", + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + for pin_name in ["A", "B", "C"]: + pin = self.nand_inst.get_pin(pin_name) + self.add_layout_pin_rect_center(text=pin_name, + layer=pin.layer, + offset=pin.center(), + width=pin.width(), + height=pin.height()) + + def analytical_delay(self, corner, slew, load=0.0): + """ Calculate the analytical delay of DFF-> INV -> INV """ + nand_delay = self.nand.analytical_delay(corner, + slew=slew, + load=self.inv.input_load()) + inv_delay = self.inv.analytical_delay(corner, + slew=nand_delay.slew, + load=load) + return nand_delay + inv_delay + diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index efcbe369..db5a1f28 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -133,7 +133,6 @@ class pnand3(pgate.pgate): # This is the extra space needed to ensure DRC rules # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") - extra_contact_space = max(-nmos.get_pin("D").by(), 0) def create_ptx(self): """ diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py new file mode 100644 index 00000000..ba909dce --- /dev/null +++ b/compiler/pgates/pnand4.py @@ -0,0 +1,372 @@ +# 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 pgate +import debug +from tech import drc, parameter, spice +from vector import vector +import logical_effort +from sram_factory import factory +from globals import OPTS +import contact + + +class pnand4(pgate.pgate): + """ + This module generates gds of a parametrically sized 4-input nand. + This model use ptx to generate a 4-input nand within a cetrain height. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + """ Creates a cell for a simple 3 input nand """ + + debug.info(2, + "creating pnand4 structure {0} with size of {1}".format(name, + size)) + self.add_comment("size: {}".format(size)) + + # We have trouble pitch matching a 3x sizes to the bitcell... + # If we relax this, we could size this better. + self.size = size + self.nmos_size = 2 * size + self.pmos_size = parameter["beta"] * size + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + + # FIXME: Allow these to be sized + debug.check(size == 1, + "Size 1 pnand4 is only supported now.") + self.tx_mults = 1 + + if OPTS.tech_name == "sky130": + self.nmos_width = self.nearest_bin("nmos", self.nmos_width) + self.pmos_width = self.nearest_bin("pmos", self.pmos_width) + + # Creates the netlist and layout + super().__init__(name, height, add_wells) + + def add_pins(self): + """ Adds pins for spice netlist """ + pin_list = ["A", "B", "C", "D", "Z", "vdd", "gnd"] + dir_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + self.add_pin_list(pin_list, dir_list) + + def create_netlist(self): + self.add_pins() + self.add_ptx() + self.create_ptx() + + def create_layout(self): + """ Calls all functions related to the generation of the layout """ + + self.setup_layout_constants() + self.place_ptx() + if self.add_wells: + self.add_well_contacts() + self.route_inputs() + self.route_output() + self.determine_width() + self.route_supply_rails() + self.connect_rails() + self.extend_wells() + self.add_boundary() + + def add_ptx(self): + """ Create the PMOS and NMOS transistors. """ + self.nmos_center = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_source_contact="active", + add_drain_contact="active") + self.add_mod(self.nmos_center) + + self.nmos_right = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_source_contact="active", + add_drain_contact=self.route_layer) + self.add_mod(self.nmos_right) + + self.nmos_left = factory.create(module_type="ptx", + width=self.nmos_width, + mults=self.tx_mults, + tx_type="nmos", + add_source_contact=self.route_layer, + add_drain_contact="active") + self.add_mod(self.nmos_left) + + self.pmos_left = factory.create(module_type="ptx", + width=self.pmos_width, + mults=self.tx_mults, + tx_type="pmos", + add_source_contact=self.route_layer, + add_drain_contact=self.route_layer) + self.add_mod(self.pmos_left) + + self.pmos_center = factory.create(module_type="ptx", + width=self.pmos_width, + mults=self.tx_mults, + tx_type="pmos", + add_source_contact=self.route_layer, + add_drain_contact=self.route_layer) + self.add_mod(self.pmos_center) + + self.pmos_right = factory.create(module_type="ptx", + width=self.pmos_width, + mults=self.tx_mults, + tx_type="pmos", + add_source_contact=self.route_layer, + add_drain_contact=self.route_layer) + self.add_mod(self.pmos_right) + + def setup_layout_constants(self): + """ Pre-compute some handy layout parameters. """ + + # Compute the overlap of the source and drain pins + self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center() + + # This is the extra space needed to ensure DRC rules + # to the active contacts + nmos = factory.create(module_type="ptx", tx_type="nmos") + extra_contact_space = max(-nmos.get_pin("D").by(), 0) + + def create_ptx(self): + """ + Create the PMOS and NMOS in the netlist. + """ + + self.pmos1_inst = self.add_inst(name="pnand4_pmos1", + mod=self.pmos_left) + self.connect_inst(["vdd", "A", "Z", "vdd"]) + + self.pmos2_inst = self.add_inst(name="pnand4_pmos2", + mod=self.pmos_center) + self.connect_inst(["Z", "B", "vdd", "vdd"]) + + self.pmos3_inst = self.add_inst(name="pnand4_pmos3", + mod=self.pmos_center) + self.connect_inst(["Z", "C", "vdd", "vdd"]) + + self.pmos4_inst = self.add_inst(name="pnand4_pmos4", + mod=self.pmos_right) + self.connect_inst(["Z", "D", "vdd", "vdd"]) + + self.nmos1_inst = self.add_inst(name="pnand4_nmos1", + mod=self.nmos_left) + self.connect_inst(["Z", "D", "net1", "gnd"]) + + self.nmos2_inst = self.add_inst(name="pnand4_nmos2", + mod=self.nmos_center) + self.connect_inst(["net1", "C", "net2", "gnd"]) + + self.nmos3_inst = self.add_inst(name="pnand4_nmos3", + mod=self.nmos_center) + self.connect_inst(["net2", "B", "net3", "gnd"]) + + self.nmos4_inst = self.add_inst(name="pnand4_nmos4", + mod=self.nmos_right) + self.connect_inst(["net3", "A", "gnd", "gnd"]) + + def place_ptx(self): + """ + Place the PMOS and NMOS in the layout at the upper-most + and lowest position to provide maximum routing in channel + """ + + pmos1_pos = vector(self.pmos_left.active_offset.x, + self.height - self.pmos_left.active_height - self.top_bottom_space) + self.pmos1_inst.place(pmos1_pos) + + pmos2_pos = pmos1_pos + self.ptx_offset + self.pmos2_inst.place(pmos2_pos) + + pmos3_pos = pmos2_pos + self.ptx_offset + self.pmos3_inst.place(pmos3_pos) + + self.pmos4_pos = pmos3_pos + self.ptx_offset + self.pmos4_inst.place(self.pmos4_pos) + + nmos1_pos = vector(self.pmos_left.active_offset.x, + self.top_bottom_space) + self.nmos1_inst.place(nmos1_pos) + + nmos2_pos = nmos1_pos + self.ptx_offset + self.nmos2_inst.place(nmos2_pos) + + nmos3_pos = nmos2_pos + self.ptx_offset + self.nmos3_inst.place(nmos3_pos) + + self.nmos4_pos = nmos3_pos + self.ptx_offset + self.nmos4_inst.place(self.nmos4_pos) + + def add_well_contacts(self): + """ Add n/p well taps to the layout and connect to supplies """ + + self.add_nwell_contact(self.pmos_right, + self.pmos4_pos + vector(self.m1_pitch, 0)) + self.add_pwell_contact(self.nmos_right, + self.nmos4_pos + vector(self.m1_pitch, 0)) + + def connect_rails(self): + """ Connect the nmos and pmos to its respective power rails """ + + self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd") + + self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd") + + self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd") + + self.connect_pin_to_rail(self.pmos4_inst, "D", "vdd") + + def route_inputs(self): + """ Route the A and B and C inputs """ + + # We can use this pitch because the contacts and overlap won't be adjacent + pmos_drain_bottom = self.pmos1_inst.get_pin("D").by() + self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space + + bottom_pin = self.nmos1_inst.get_pin("D") + # active contact metal to poly contact metal spacing + active_contact_to_poly_contact = bottom_pin.uy() + self.m1_space + 0.5 * contact.poly_contact.second_layer_height + # active diffusion to poly contact spacing + # doesn't use nmos uy because that is calculated using offset + poly height + active_top = self.nmos1_inst.by() + self.nmos1_inst.mod.active_height + active_to_poly_contact = active_top + self.poly_to_active + 0.5 * contact.poly_contact.first_layer_height + active_to_poly_contact2 = active_top + self.poly_contact_to_gate + 0.5 * self.route_layer_width + self.inputA_yoffset = max(active_contact_to_poly_contact, + active_to_poly_contact, + active_to_poly_contact2) + + apin = self.route_input_gate(self.pmos1_inst, + self.nmos1_inst, + self.inputA_yoffset, + "A", + position="left") + + self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch + bpin = self.route_input_gate(self.pmos2_inst, + self.nmos2_inst, + self.inputB_yoffset, + "B", + position="center") + + self.inputC_yoffset = self.inputB_yoffset + self.m3_pitch + cpin = self.route_input_gate(self.pmos3_inst, + self.nmos3_inst, + self.inputC_yoffset, + "C", + position="right") + + self.inputD_yoffset = self.inputC_yoffset + self.m3_pitch + cpin = self.route_input_gate(self.pmos4_inst, + self.nmos4_inst, + self.inputD_yoffset, + "D", + position="right") + + if OPTS.tech_name == "sky130": + self.add_enclosure([apin, bpin, cpin], "npc", drc("npc_enclose_poly")) + + def route_output(self): + """ Route the Z output """ + + # PMOS1 drain + pmos1_pin = self.pmos1_inst.get_pin("D") + # PMOS3 drain + pmos3_pin = self.pmos3_inst.get_pin("D") + # NMOS3 drain + nmos4_pin = self.nmos4_inst.get_pin("D") + + out_offset = vector(nmos4_pin.cx() + self.route_layer_pitch, + self.output_yoffset) + + # Go up to metal2 for ease on all output pins + # self.add_via_center(layers=self.m1_stack, + # offset=pmos1_pin.center(), + # directions=("V", "V")) + # self.add_via_center(layers=self.m1_stack, + # offset=pmos3_pin.center(), + # directions=("V", "V")) + # self.add_via_center(layers=self.m1_stack, + # offset=nmos3_pin.center(), + # directions=("V", "V")) + + # # Route in the A input track (top track) + # mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) + # self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) + + # This extends the output to the edge of the cell + # self.add_via_center(layers=self.m1_stack, + # offset=mid_offset) + + top_left_pin_offset = pmos1_pin.center() + top_right_pin_offset = pmos3_pin.center() + bottom_pin_offset = nmos4_pin.center() + + # PMOS1 to output + self.add_path(self.route_layer, [top_left_pin_offset, + vector(top_left_pin_offset.x, out_offset.y), + out_offset]) + # PMOS4 to output + self.add_path(self.route_layer, [top_right_pin_offset, + vector(top_right_pin_offset.x, out_offset.y), + out_offset]) + # NMOS4 to output + mid2_offset = vector(out_offset.x, bottom_pin_offset.y) + self.add_path(self.route_layer, + [bottom_pin_offset, mid2_offset], + width=nmos4_pin.height()) + mid3_offset = vector(out_offset.x, nmos4_pin.by()) + self.add_path(self.route_layer, [mid3_offset, out_offset]) + + self.add_layout_pin_rect_center(text="Z", + layer=self.route_layer, + offset=out_offset) + + def analytical_power(self, corner, load): + """Returns dynamic and leakage power. Results in nW""" + c_eff = self.calculate_effective_capacitance(load) + freq = spice["default_event_frequency"] + power_dyn = self.calc_dynamic_power(corner, c_eff, freq) + power_leak = spice["nand4_leakage"] + + total_power = self.return_power(power_dyn, power_leak) + return total_power + + def calculate_effective_capacitance(self, load): + """Computes effective capacitance. Results in fF""" + c_load = load + # In fF + c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"]) + transition_prob = 0.1094 + return transition_prob * (c_load + c_para) + + def input_load(self): + """Return the relative input capacitance of a single input""" + return self.nmos_size + self.pmos_size + + def get_stage_effort(self, cout, inp_is_rise=True): + """ + Returns an object representing the parameters for delay in tau units. + Optional is_rise refers to the input direction rise/fall. + Input inverted by this stage. + """ + parasitic_delay = 3 + return logical_effort.logical_effort(self.name, + self.size, + self.input_load(), + cout, + parasitic_delay, + not inp_is_rise) + + def build_graph(self, graph, inst_name, port_nets): + """ + Adds edges based on inputs/outputs. + Overrides base class function. + """ + self.add_graph_edges(graph, port_nets) diff --git a/compiler/tests/04_pand2_test.py b/compiler/tests/04_pand2_test.py index f7e5f304..077c180e 100755 --- a/compiler/tests/04_pand2_test.py +++ b/compiler/tests/04_pand2_test.py @@ -8,13 +8,13 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -from sram_factory import factory import debug + class pand2_test(openram_test): def runTest(self): diff --git a/compiler/tests/04_pand3_test.py b/compiler/tests/04_pand3_test.py index e58f1ee9..4817601e 100755 --- a/compiler/tests/04_pand3_test.py +++ b/compiler/tests/04_pand3_test.py @@ -8,13 +8,13 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS -from sram_factory import factory import debug + class pand3_test(openram_test): def runTest(self): diff --git a/compiler/tests/04_pand4_test.py b/compiler/tests/04_pand4_test.py new file mode 100755 index 00000000..f7dd329d --- /dev/null +++ b/compiler/tests/04_pand4_test.py @@ -0,0 +1,39 @@ +#!/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 +import debug + + +class pand4_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + global verify + import verify + + import pand4 + + debug.info(2, "Testing pand4 gate 4x") + a = pand4.pand4(name="pand4x4", size=4) + self.local_check(a) + + globals.end_openram() + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_pnand4_test.py b/compiler/tests/04_pnand4_test.py new file mode 100755 index 00000000..d2c64a5b --- /dev/null +++ b/compiler/tests/04_pnand4_test.py @@ -0,0 +1,42 @@ +#!/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 pnand4_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(2, "Checking 4-input nand gate") + tx = factory.create(module_type="pnand4", size=1) + self.local_check(tx) + + # debug.info(2, "Checking 3-input nand gate") + # tx = factory.create(module_type="pnand3", size=1, add_wells=False) + # # Only DRC because well contacts will fail LVS + # self.local_drc_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()) From f8146e3f699537516eae7981876bab7a1c422777 Mon Sep 17 00:00:00 2001 From: mrg Date: Fri, 2 Oct 2020 15:52:09 -0700 Subject: [PATCH 02/12] Add decoder4x16 --- compiler/modules/hierarchical_predecode.py | 11 ++++++++--- compiler/modules/hierarchical_predecode4x16.py | 16 ++++++++-------- compiler/pgates/pand4.py | 2 +- .../tests/06_hierarchical_predecode4x16_test.py | 5 +++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 9c34735d..504b3771 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -44,7 +44,7 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and AND gate modules """ - debug.check(self.number_of_inputs < 4, + debug.check(self.number_of_inputs <= 4, "Invalid number of predecode inputs: {}".format(self.number_of_inputs)) if self.column_decoder: @@ -203,6 +203,7 @@ class hierarchical_predecode(design.design): pin = top_and_gate.get_pin("D") else: debug.error("Too many inputs for predecoder.", -1) + y_offset = pin.cy() in_pin = "in_{}".format(num) a_pin = "A_{}".format(num) @@ -283,10 +284,14 @@ class hierarchical_predecode(design.design): if self.number_of_inputs == 2: gate_lst = ["A", "B"] - else: + elif self.number_of_inputs == 3: gate_lst = ["A", "B", "C"] + elif self.number_of_inputs == 4: + gate_lst = ["A", "B", "C", "D"] + else: + debug.error("Invalid number of nand inputs for decode", -1) - # this will connect pins A,B or A,B,C + # this will connect pins A,B or A,B,C or A,B,C,D for rail_pin, gate_pin in zip(index_lst, gate_lst): pin = self.and_inst[k].get_pin(gate_pin) pin_pos = pin.center() diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py index 3b423fde..93dbc4ea 100644 --- a/compiler/modules/hierarchical_predecode4x16.py +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -32,14 +32,14 @@ class hierarchical_predecode4x16(hierarchical_predecode): ["in_0", "inbar_1", "in_2", "inbar_3", "out_5", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "inbar_3", "out_6", "vdd", "gnd"], ["in_0", "in_1", "in_2", "inbar_3", "out_7", "vdd", "gnd"], - ["inbar_0", "inbar_1", "inbar_2", "in_3", "out_0", "vdd", "gnd"], - ["in_0", "inbar_1", "inbar_2", "in_3", "out_1", "vdd", "gnd"], - ["inbar_0", "in_1", "inbar_2", "in_3", "out_2", "vdd", "gnd"], - ["in_0", "in_1", "inbar_2", "in_3", "out_3", "vdd", "gnd"], - ["inbar_0", "inbar_1", "in_2", "in_3", "out_4", "vdd", "gnd"], - ["in_0", "inbar_1", "in_2", "in_3", "out_5", "vdd", "gnd"], - ["inbar_0", "in_1", "in_2", "in_3", "out_6", "vdd", "gnd"], - ["in_0", "in_1", "in_2", "in_3", "out_7", "vdd", "gnd"] ] + ["inbar_0", "inbar_1", "inbar_2", "in_3", "out_8", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "in_3", "out_9", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "in_3", "out_10", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "in_3", "out_11", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "in_3", "out_12", "vdd", "gnd"], + ["in_0", "inbar_1", "in_2", "in_3", "out_13", "vdd", "gnd"], + ["inbar_0", "in_1", "in_2", "in_3", "out_14", "vdd", "gnd"], + ["in_0", "in_1", "in_2", "in_3", "out_15", "vdd", "gnd"] ] self.create_and_array(connections) diff --git a/compiler/pgates/pand4.py b/compiler/pgates/pand4.py index 54d2890e..021ccf6c 100644 --- a/compiler/pgates/pand4.py +++ b/compiler/pgates/pand4.py @@ -145,7 +145,7 @@ class pand4(pgate.pgate): width=pin.width(), height=pin.height()) - for pin_name in ["A", "B", "C"]: + for pin_name in ["A", "B", "C", "D"]: pin = self.nand_inst.get_pin(pin_name) self.add_layout_pin_rect_center(text=pin_name, layer=pin.layer, diff --git a/compiler/tests/06_hierarchical_predecode4x16_test.py b/compiler/tests/06_hierarchical_predecode4x16_test.py index b4ebda38..7ded6144 100755 --- a/compiler/tests/06_hierarchical_predecode4x16_test.py +++ b/compiler/tests/06_hierarchical_predecode4x16_test.py @@ -8,14 +8,15 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory import debug -@unittest.skip("SKIPPING hierarchical_predecode4x16_test") + +# @unittest.skip("SKIPPING hierarchical_predecode4x16_test") class hierarchical_predecode4x16_test(openram_test): def runTest(self): From c06b02e6fcf9a89257885c498b8e8fb981864608 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 5 Oct 2020 08:56:51 -0700 Subject: [PATCH 03/12] Rename single_level_column_mux to just column_mux --- ...el_column_mux_array.py => column_mux_array.py} | 4 ++-- compiler/options.py | 4 ++-- .../{single_level_column_mux.py => column_mux.py} | 5 ++--- compiler/tests/04_and4_dec_test.py | 4 ++-- ...rw_1r_test.py => 04_column_mux_1rw_1r_test.py} | 8 ++++---- ...ell_test.py => 04_column_mux_pbitcell_test.py} | 9 ++++----- ...l_column_mux_test.py => 04_column_mux_test.py} | 6 +++--- ...test.py => 07_column_mux_array_1rw_1r_test.py} | 15 ++++++++------- ...st.py => 07_column_mux_array_pbitcell_test.py} | 14 +++++++------- ..._array_test.py => 07_column_mux_array_test.py} | 9 +++++---- 10 files changed, 39 insertions(+), 39 deletions(-) rename compiler/modules/{single_level_column_mux_array.py => column_mux_array.py} (98%) rename compiler/pgates/{single_level_column_mux.py => column_mux.py} (98%) rename compiler/tests/{04_single_level_column_mux_1rw_1r_test.py => 04_column_mux_1rw_1r_test.py} (79%) rename compiler/tests/{04_single_level_column_mux_pbitcell_test.py => 04_column_mux_pbitcell_test.py} (79%) rename compiler/tests/{04_single_level_column_mux_test.py => 04_column_mux_test.py} (87%) rename compiler/tests/{07_single_level_column_mux_array_1rw_1r_test.py => 07_column_mux_array_1rw_1r_test.py} (65%) rename compiler/tests/{07_single_level_column_mux_array_pbitcell_test.py => 07_column_mux_array_pbitcell_test.py} (69%) rename compiler/tests/{07_single_level_column_mux_array_test.py => 07_column_mux_array_test.py} (77%) diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/column_mux_array.py similarity index 98% rename from compiler/modules/single_level_column_mux_array.py rename to compiler/modules/column_mux_array.py index 1be0b378..87e750cf 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/column_mux_array.py @@ -14,7 +14,7 @@ from globals import OPTS from tech import cell_properties -class single_level_column_mux_array(design.design): +class column_mux_array(design.design): """ Dynamically generated column mux array. Array of column mux to read the bitlines through the 6T. @@ -89,7 +89,7 @@ class single_level_column_mux_array(design.design): self.add_pin("gnd") def add_modules(self): - self.mux = factory.create(module_type="single_level_column_mux", + self.mux = factory.create(module_type="column_mux", bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br) self.add_mod(self.mux) diff --git a/compiler/options.py b/compiler/options.py index b9be3999..7f86a64f 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -143,7 +143,7 @@ class options(optparse.Values): bitcell_array = "bitcell_array" bitcell = "bitcell" buf_dec = "pbuf" - column_mux_array = "single_level_column_mux_array" + column_mux_array = "column_mux_array" control_logic = "control_logic" decoder = "hierarchical_decoder" delay_chain = "delay_chain" @@ -152,7 +152,7 @@ class options(optparse.Values): inv_dec = "pinv" nand2_dec = "pnand2" nand3_dec = "pnand3" - nand4_dec = "pnand4" # Not available right now + nand4_dec = "pnand4" precharge_array = "precharge_array" ptx = "ptx" replica_bitline = "replica_bitline" diff --git a/compiler/pgates/single_level_column_mux.py b/compiler/pgates/column_mux.py similarity index 98% rename from compiler/pgates/single_level_column_mux.py rename to compiler/pgates/column_mux.py index 4873e6fc..20616115 100644 --- a/compiler/pgates/single_level_column_mux.py +++ b/compiler/pgates/column_mux.py @@ -10,14 +10,13 @@ import debug from tech import drc, layer from vector import vector from sram_factory import factory -import logical_effort from globals import OPTS -class single_level_column_mux(pgate.pgate): +class column_mux(pgate.pgate): """ This module implements the columnmux bitline cell used in the design. - Creates a single columnmux cell with the given integer size relative + Creates a single column mux cell with the given integer size relative to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: Column-mux transistors driven by the decoder must be sized for optimal speed diff --git a/compiler/tests/04_and4_dec_test.py b/compiler/tests/04_and4_dec_test.py index ffd7788a..aa163160 100755 --- a/compiler/tests/04_and4_dec_test.py +++ b/compiler/tests/04_and4_dec_test.py @@ -8,7 +8,7 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -16,7 +16,7 @@ from sram_factory import factory import debug -@unittest.skip("SKIPPING 04_and4_dec_test") +# @unittest.skip("SKIPPING 04_and4_dec_test") class and4_dec_test(openram_test): def runTest(self): diff --git a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py b/compiler/tests/04_column_mux_1rw_1r_test.py similarity index 79% rename from compiler/tests/04_single_level_column_mux_1rw_1r_test.py rename to compiler/tests/04_column_mux_1rw_1r_test.py index a7e79e9b..7825e081 100755 --- a/compiler/tests/04_single_level_column_mux_1rw_1r_test.py +++ b/compiler/tests/04_column_mux_1rw_1r_test.py @@ -8,7 +8,7 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -16,7 +16,7 @@ from sram_factory import factory import debug -class single_level_column_mux_1rw_1r_test(openram_test): +class column_mux_1rw_1r_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -28,11 +28,11 @@ class single_level_column_mux_1rw_1r_test(openram_test): globals.setup_bitcell() debug.info(2, "Checking column mux port 0") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) debug.info(2, "Checking column mux port 1") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1") + tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl1", bitcell_br="br1") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/04_single_level_column_mux_pbitcell_test.py b/compiler/tests/04_column_mux_pbitcell_test.py similarity index 79% rename from compiler/tests/04_single_level_column_mux_pbitcell_test.py rename to compiler/tests/04_column_mux_pbitcell_test.py index 18ab631f..a7a93403 100755 --- a/compiler/tests/04_single_level_column_mux_pbitcell_test.py +++ b/compiler/tests/04_column_mux_pbitcell_test.py @@ -8,16 +8,15 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 04_driver_test") -class single_level_column_mux_pbitcell_test(openram_test): +class column_mux_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -31,12 +30,12 @@ class single_level_column_mux_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking column mux for pbitcell (innermost connections)") - tx = factory.create(module_type="single_level_column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") + tx = factory.create(module_type="column_mux", tx_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(tx) factory.reset() debug.info(2, "Checking column mux for pbitcell (outermost connections)") - tx = factory.create(module_type="single_level_column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") + tx = factory.create(module_type="column_mux",tx_size=8, bitcell_bl="bl2", bitcell_br="br2") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/04_single_level_column_mux_test.py b/compiler/tests/04_column_mux_test.py similarity index 87% rename from compiler/tests/04_single_level_column_mux_test.py rename to compiler/tests/04_column_mux_test.py index 20dfe968..a10603ee 100755 --- a/compiler/tests/04_single_level_column_mux_test.py +++ b/compiler/tests/04_column_mux_test.py @@ -8,7 +8,7 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS @@ -16,7 +16,7 @@ from sram_factory import factory import debug -class single_level_column_mux_test(openram_test): +class column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -24,7 +24,7 @@ class single_level_column_mux_test(openram_test): # check single level column mux in single port debug.info(2, "Checking column mux") - tx = factory.create(module_type="single_level_column_mux", tx_size=8) + tx = factory.create(module_type="column_mux", tx_size=8) self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py b/compiler/tests/07_column_mux_array_1rw_1r_test.py similarity index 65% rename from compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py rename to compiler/tests/07_column_mux_array_1rw_1r_test.py index 209133aa..10c96092 100755 --- a/compiler/tests/07_single_level_column_mux_array_1rw_1r_test.py +++ b/compiler/tests/07_column_mux_array_1rw_1r_test.py @@ -14,7 +14,8 @@ from globals import OPTS from sram_factory import factory import debug -class single_level_column_mux_test(openram_test): + +class column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -26,27 +27,27 @@ class single_level_column_mux_test(openram_test): globals.setup_bitcell() debug.info(1, "Testing sample for 2-way column_mux_array port 0") - a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 2-way column_mux_array port 1") - a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1") + a = factory.create(module_type="column_mux_array", columns=8, word_size=4, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array port 0") - a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array port 1") - a = factory.create(module_type="single_level_column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + a = factory.create(module_type="column_mux_array", columns=8, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array port 0") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=2, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="column_mux_array", columns=16, word_size=2, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array port 1") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=2, bitcell_bl="bl1", bitcell_br="br1") + a = factory.create(module_type="column_mux_array", columns=16, word_size=2, bitcell_bl="bl1", bitcell_br="br1") self.local_check(a) globals.end_openram() diff --git a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py b/compiler/tests/07_column_mux_array_pbitcell_test.py similarity index 69% rename from compiler/tests/07_single_level_column_mux_array_pbitcell_test.py rename to compiler/tests/07_column_mux_array_pbitcell_test.py index 663ff075..0a089bb9 100755 --- a/compiler/tests/07_single_level_column_mux_array_pbitcell_test.py +++ b/compiler/tests/07_column_mux_array_pbitcell_test.py @@ -7,19 +7,19 @@ # All rights reserved. # from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory import debug -class single_level_column_mux_pbitcell_test(openram_test): + +class column_mux_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - import single_level_column_mux_array # check single level column mux array in multi-port OPTS.bitcell = "pbitcell" @@ -29,19 +29,19 @@ class single_level_column_mux_pbitcell_test(openram_test): factory.reset() debug.info(1, "Testing sample for 2-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="column_mux_array", columns=16, word_size=8, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array in multi-port") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="column_mux_array", columns=16, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (innermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") + a = factory.create(module_type="column_mux_array", columns=32, word_size=4, bitcell_bl="bl0", bitcell_br="br0") self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array in multi-port (outermost connections)") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3) + a = factory.create(module_type="column_mux_array", columns=32, word_size=4, bitcell_bl="bl2", bitcell_br="br2", column_offset=3) self.local_check(a) globals.end_openram() diff --git a/compiler/tests/07_single_level_column_mux_array_test.py b/compiler/tests/07_column_mux_array_test.py similarity index 77% rename from compiler/tests/07_single_level_column_mux_array_test.py rename to compiler/tests/07_column_mux_array_test.py index c0476a4f..f32c2773 100755 --- a/compiler/tests/07_single_level_column_mux_array_test.py +++ b/compiler/tests/07_column_mux_array_test.py @@ -14,22 +14,23 @@ from globals import OPTS from sram_factory import factory import debug -class single_level_column_mux_test(openram_test): + +class column_mux_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) debug.info(1, "Testing sample for 2-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=8) + a = factory.create(module_type="column_mux_array", columns=16, word_size=8) self.local_check(a) debug.info(1, "Testing sample for 4-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=16, word_size=4) + a = factory.create(module_type="column_mux_array", columns=16, word_size=4) self.local_check(a) debug.info(1, "Testing sample for 8-way column_mux_array") - a = factory.create(module_type="single_level_column_mux_array", columns=32, word_size=4) + a = factory.create(module_type="column_mux_array", columns=32, word_size=4) self.local_check(a) globals.end_openram() From 4a58f09c1cdedb4220214aac9d964e3dbd352182 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 5 Oct 2020 10:52:56 -0700 Subject: [PATCH 04/12] Use 4x16 decoder with dual port bitcell in tests. --- compiler/custom/and4_dec.py | 2 +- compiler/modules/hierarchical_decoder.py | 127 +++++++++++++++--- compiler/pgates/pnand4.py | 11 +- compiler/tests/04_pnand4_test.py | 5 - .../tests/06_hierarchical_decoder_test.py | 14 +- 5 files changed, 124 insertions(+), 35 deletions(-) diff --git a/compiler/custom/and4_dec.py b/compiler/custom/and4_dec.py index 9c68f78b..211e4ce4 100644 --- a/compiler/custom/and4_dec.py +++ b/compiler/custom/and4_dec.py @@ -122,7 +122,7 @@ class and4_dec(design.design): width=pin.width(), height=pin.height()) - for pin_name in ["A", "B", "C"]: + for pin_name in ["A", "B", "C", "D"]: pin = self.nand_inst.get_pin(pin_name) self.add_layout_pin_rect_center(text=pin_name, layer=pin.layer, diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 21aa1c89..51a77bc6 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -24,13 +24,14 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] + self.pre4x16_inst = [] b = factory.create(module_type="bitcell") self.cell_height = b.height self.num_outputs = num_outputs self.num_inputs = math.ceil(math.log(self.num_outputs, 2)) - (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) + (self.no_of_pre2x4, self.no_of_pre3x8, self.no_of_pre4x16)=self.determine_predecodes(self.num_inputs) self.create_netlist() if not OPTS.netlist_only: @@ -86,25 +87,37 @@ class hierarchical_decoder(design.design): height=self.cell_height) self.add_mod(self.pre3_8) + self.pre4_16 = factory.create(module_type="hierarchical_predecode4x16", + height=self.cell_height) + self.add_mod(self.pre4_16) + def determine_predecodes(self, num_inputs): - """ Determines the number of 2:4 pre-decoder and 3:8 pre-decoder - needed based on the number of inputs """ + """ + Determines the number of 2:4, 3:8 and 4:16 pre-decoders + needed based on the number of inputs + """ if (num_inputs == 2): - return (1, 0) + return (1, 0, 0) elif (num_inputs == 3): - return(0, 1) + return(0, 1, 0) elif (num_inputs == 4): - return(2, 0) + return(2, 0, 0) elif (num_inputs == 5): - return(1, 1) + return(1, 1, 0) elif (num_inputs == 6): - return(3, 0) + return(3, 0, 0) elif (num_inputs == 7): - return(2, 1) + return(2, 1, 0) elif (num_inputs == 8): - return(1, 2) + return(1, 2, 0) elif (num_inputs == 9): - return(0, 3) + return(0, 3, 0) + elif (num_inputs == 10): + return(0, 2, 1) + elif (num_inputs == 11): + return(0, 1, 2) + elif (num_inputs == 12): + return(0, 0, 3) else: debug.error("Invalid number of inputs for hierarchical decoder", -1) @@ -131,12 +144,19 @@ class hierarchical_decoder(design.design): index = index + 1 self.predec_groups.append(lines) + for i in range(self.no_of_pre4x16): + lines = [] + for j in range(16): + lines.append(index) + index = index + 1 + self.predec_groups.append(lines) + def setup_layout_constants(self): """ Calculate the overall dimensions of the hierarchical decoder """ # If we have 4 or fewer rows, the predecoder is the decoder itself if self.num_inputs>=4: - self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8 + self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8 + 16 * self.no_of_pre4x16 else: self.total_number_of_predecoder_outputs = 0 debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs), @@ -144,17 +164,20 @@ class hierarchical_decoder(design.design): # Calculates height and width of pre-decoder, # FIXME: Update with 4x16 - if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0: - self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width) - elif self.no_of_pre3x8 > 0: - self.predecoder_width = self.pre3_8.width - else: - self.predecoder_width = self.pre2_4.width + self.predecoder_width = 0 + if self.no_of_pre2x4 > 0: + self.predecoder_width = max(self.predecoder_width, self.pre2_4.width) + if self.no_of_pre3x8 > 0: + self.predecoder_width = max(self.predecoder_width, self.pre3_8.width) + if self.no_of_pre4x16 > 0: + self.predecoder_width = max(self.predecoder_width, self.pre4_16.width) # How much space between each predecoder self.predecoder_spacing = 2 * self.and2.height - self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \ - + (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing + self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 \ + + self.pre3_8.height * self.no_of_pre3x8 \ + + self.pre4_16.height * self.no_of_pre4x16 \ + + (self.no_of_pre2x4 + self.no_of_pre3x8 + self.no_of_pre4x16 - 1) * self.predecoder_spacing # Inputs to cells are on input layer # Outputs from cells are on output layer @@ -192,6 +215,8 @@ class hierarchical_decoder(design.design): min_x = min(min_x, self.pre2x4_inst[0].lx()) if self.no_of_pre3x8 > 0: min_x = min(min_x, self.pre3x8_inst[0].lx()) + if self.no_of_pre4x16 > 0: + min_x = min(min_x, self.pre4x16_inst[0].lx()) input_offset=vector(min_x - self.input_routing_width, 0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] @@ -232,6 +257,20 @@ class hierarchical_decoder(design.design): self.route_input_bus(decoder_offset, input_offset) + for pre_num in range(self.no_of_pre4x16): + for i in range(4): + index = pre_num * 4 + i + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2 + + input_pos = self.input_bus["addr_{}".format(index)].center() + + in_name = "in_{}".format(i) + decoder_pin = self.pre4x16_inst[pre_num].get_pin(in_name) + + decoder_offset = decoder_pin.center() + input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) + + self.route_input_bus(decoder_offset, input_offset) + def route_input_bus(self, input_offset, output_offset): """ Route a vertical M2 coordinate to another @@ -267,6 +306,9 @@ class hierarchical_decoder(design.design): for i in range(self.no_of_pre3x8): self.create_pre3x8(i) + for i in range(self.no_of_pre4x16): + self.create_pre4x16(i) + def create_pre2x4(self, num): """ Add a 2x4 predecoder to the left of the origin """ @@ -305,6 +347,24 @@ class hierarchical_decoder(design.design): mod=self.pre3_8)) self.connect_inst(pins) + def create_pre4x16(self, num): + """ Add 4x16 predecoder to the left of the origin and above any 3x8 decoders """ + # If we had 2x4 predecodes, those are used as the lower + # decode output bits + in_index_offset = num * 4 + self.no_of_pre3x8 * 3 + self.no_of_pre2x4 * 2 + out_index_offset = num * 16 + self.no_of_pre3x8 * 8 + self.no_of_pre2x4 * 4 + + pins = [] + for input_index in range(4): + pins.append("addr_{0}".format(input_index + in_index_offset)) + for output_index in range(16): + pins.append("out_{0}".format(output_index + out_index_offset)) + pins.extend(["vdd", "gnd"]) + + self.pre4x16_inst.append(self.add_inst(name="pre4x16_{0}".format(num), + mod=self.pre4_16)) + self.connect_inst(pins) + def place_pre_decoder(self): """ Creates pre-decoder and places labels input address [A] """ @@ -314,11 +374,16 @@ class hierarchical_decoder(design.design): for i in range(self.no_of_pre3x8): self.place_pre3x8(i) + for i in range(self.no_of_pre4x16): + self.place_pre4x16(i) + self.predecode_height = 0 if self.no_of_pre2x4 > 0: self.predecode_height = self.pre2x4_inst[-1].uy() if self.no_of_pre3x8 > 0: self.predecode_height = self.pre3x8_inst[-1].uy() + if self.no_of_pre4x16 > 0: + self.predecode_height = self.pre4x16_inst[-1].uy() def place_pre2x4(self, num): """ Place 2x4 predecoder to the left of the origin """ @@ -333,6 +398,14 @@ class hierarchical_decoder(design.design): offset = vector(-self.pre3_8.width, height) self.pre3x8_inst[num].place(offset) + def place_pre4x16(self, num): + """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ + height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \ + + self.no_of_pre3x8 * (self.pre3_8.height + self.predecoder_spacing) \ + + num * (self.pre4_16.height + self.predecoder_spacing) + offset = vector(-self.pre4_16.width, height) + self.pre4x16_inst[num].place(offset) + def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters and add the primary decoder output pins. """ @@ -468,7 +541,17 @@ class hierarchical_decoder(design.design): x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) - + + # FIXME: convert to connect_bus + for pre_num in range(self.no_of_pre4x16): + for i in range(16): + predecode_name = "predecode_{}".format(pre_num * 16 + i + self.no_of_pre3x8 * 8 + self.no_of_pre2x4 * 4) + out_name = "out_{}".format(i) + pin = self.pre4x16_inst[pre_num].get_pin(out_name) + x_offset = self.pre4x16_inst[pre_num].rx() + self.output_layer_pitch + y_offset = self.pre4x16_inst[pre_num].by() + i * self.cell_height + self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset) + def route_bus_to_decoder(self): """ Use the self.predec_groups to determine the connections to the decoder AND gates. @@ -559,7 +642,7 @@ class hierarchical_decoder(design.design): start_layer=supply_pin.layer) # Copy the pins from the predecoders - for pre in self.pre2x4_inst + self.pre3x8_inst: + for pre in self.pre2x4_inst + self.pre3x8_inst + self.pre4x16_inst: for pin_name in ["vdd", "gnd"]: self.copy_layout_pin(pre, pin_name) diff --git a/compiler/pgates/pnand4.py b/compiler/pgates/pnand4.py index ba909dce..5a812799 100644 --- a/compiler/pgates/pnand4.py +++ b/compiler/pgates/pnand4.py @@ -133,7 +133,6 @@ class pnand4(pgate.pgate): # This is the extra space needed to ensure DRC rules # to the active contacts nmos = factory.create(module_type="ptx", tx_type="nmos") - extra_contact_space = max(-nmos.get_pin("D").by(), 0) def create_ptx(self): """ @@ -248,29 +247,29 @@ class pnand4(pgate.pgate): "A", position="left") - self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch + self.inputB_yoffset = self.inputA_yoffset + self.m1_pitch bpin = self.route_input_gate(self.pmos2_inst, self.nmos2_inst, self.inputB_yoffset, "B", position="center") - self.inputC_yoffset = self.inputB_yoffset + self.m3_pitch + self.inputC_yoffset = self.inputB_yoffset + self.m1_pitch cpin = self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, "C", position="right") - self.inputD_yoffset = self.inputC_yoffset + self.m3_pitch - cpin = self.route_input_gate(self.pmos4_inst, + self.inputD_yoffset = self.inputC_yoffset + self.m1_pitch + dpin = self.route_input_gate(self.pmos4_inst, self.nmos4_inst, self.inputD_yoffset, "D", position="right") if OPTS.tech_name == "sky130": - self.add_enclosure([apin, bpin, cpin], "npc", drc("npc_enclose_poly")) + self.add_enclosure([apin, bpin, cpin, dpin], "npc", drc("npc_enclose_poly")) def route_output(self): """ Route the Z output """ diff --git a/compiler/tests/04_pnand4_test.py b/compiler/tests/04_pnand4_test.py index d2c64a5b..8eb1b4ba 100755 --- a/compiler/tests/04_pnand4_test.py +++ b/compiler/tests/04_pnand4_test.py @@ -26,11 +26,6 @@ class pnand4_test(openram_test): tx = factory.create(module_type="pnand4", size=1) self.local_check(tx) - # debug.info(2, "Checking 3-input nand gate") - # tx = factory.create(module_type="pnand3", size=1, add_wells=False) - # # Only DRC because well contacts will fail LVS - # self.local_drc_check(tx) - globals.end_openram() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index e2b34cba..42bc38cd 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -8,19 +8,26 @@ # import unittest from testutils import * -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals from globals import OPTS from sram_factory import factory import debug + class hierarchical_decoder_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) + # Use the 2 port cell since it is usually bigger/easier + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + # Checks 2x4 and 2-input NAND decoder debug.info(1, "Testing 16 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=16) @@ -50,6 +57,11 @@ class hierarchical_decoder_test(openram_test): debug.info(1, "Testing 512 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=512) self.local_check(a) + + # Checks 3 x 4x16 and 4-input NAND decoder + debug.info(1, "Testing 4096 row sample for hierarchical_decoder") + a = factory.create(module_type="hierarchical_decoder", num_outputs=4096) + self.local_check(a) globals.end_openram() From 9fe63585690343935e5d802386b9cd966553dc70 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 5 Oct 2020 13:50:04 -0700 Subject: [PATCH 05/12] Change .spinit to .spiceinit --- compiler/characterizer/stimuli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index da8e16f0..785b2b3b 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -319,7 +319,7 @@ class stimuli(): # ngspice 27+ supports threading with "set num_threads=4" in the stimulus file or a .spiceinit # Measurements can't be made with a raw file set in ngspice # -r {2}timing.raw - ng_cfg = open("{}.spinit".format(OPTS.openram_temp), "w") + ng_cfg = open("{}.spiceinit".format(OPTS.openram_temp), "w") ng_cfg.write("set num_threads={}\n".format(OPTS.num_threads)) ng_cfg.close() From c4952ca8be89fb47253560dab4475be88d5348f5 Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 5 Oct 2020 13:51:20 -0700 Subject: [PATCH 06/12] Skip full sram pex test too slow --- compiler/tests/26_sram_pex_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/tests/26_sram_pex_test.py b/compiler/tests/26_sram_pex_test.py index 7736e379..5a3253de 100755 --- a/compiler/tests/26_sram_pex_test.py +++ b/compiler/tests/26_sram_pex_test.py @@ -16,6 +16,7 @@ from sram_factory import factory import debug +@unittest.skip("SKIPPING 26_sram_pex_test") class sram_pex_test(openram_test): def runTest(self): From cb35c0aff41d159272573613ebbdcd67afafcb0d Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 5 Oct 2020 15:49:00 -0700 Subject: [PATCH 07/12] Add command line -j option for number of threads. --- compiler/globals.py | 5 +++++ compiler/options.py | 1 + 2 files changed, 6 insertions(+) diff --git a/compiler/globals.py b/compiler/globals.py index dd4ac177..e1bb8661 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -55,6 +55,11 @@ def parse_args(): action="store_false", help="Disable all LVS/DRC checks", dest="check_lvsdrc"), + optparse.make_option("-j", "--threads", + action="store", + type="int", + help="Specify the number of threads (default: 2)", + dest="num_threads"), optparse.make_option("-v", "--verbose", action="count", diff --git a/compiler/options.py b/compiler/options.py index 632cd34f..2221f85f 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -9,6 +9,7 @@ import optparse import getpass import os + class options(optparse.Values): """ Class for holding all of the OpenRAM options. All From a145a37cf7df99289e13d2be3829c32679db8f3c Mon Sep 17 00:00:00 2001 From: mrg Date: Mon, 5 Oct 2020 15:56:12 -0700 Subject: [PATCH 08/12] PEP8 fixes in regress.py --- compiler/tests/regress.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index 3fd5d6eb..383ef8fa 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -9,7 +9,7 @@ import re import unittest -import sys,os +import sys, os sys.path.append(os.getenv("OPENRAM_HOME")) import globals @@ -47,12 +47,12 @@ suite = unittest.TestSuite() load = unittest.defaultTestLoader.loadTestsFromModule suite.addTests(map(load, modules)) -test_runner = unittest.TextTestRunner(verbosity=2,stream=sys.stderr) +test_runner = unittest.TextTestRunner(verbosity=2, stream=sys.stderr) test_result = test_runner.run(suite) import verify verify.print_drc_stats() verify.print_lvs_stats() -verify.print_pex_stats() +verify.print_pex_stats() sys.exit(not test_result.wasSuccessful()) From ba432669a1e98b40507687738b2c6f41b3f1bd98 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 6 Oct 2020 16:25:44 -0700 Subject: [PATCH 09/12] Add various riscv examples --- .../example_configs/riscv-freepdk45-8kbyte.py | 26 +++++++++++++++++++ .../riscv-scn4m_subm-16kbyte.py | 26 +++++++++++++++++++ .../riscv-scn4m_subm-1kbyte.py | 24 +++++++++++++++++ .../riscv-scn4m_subm-2kbyte.py | 24 +++++++++++++++++ .../riscv-scn4m_subm-32kbyte.py | 26 +++++++++++++++++++ .../riscv-scn4m_subm-4kbyte.py | 24 +++++++++++++++++ .../riscv-scn4m_subm-8kbyte.py | 26 +++++++++++++++++++ .../example_configs/riscv-sky130-1kbyte.py | 26 +++++++++++++++++++ .../example_configs/riscv-sky130-2kbyte.py | 26 +++++++++++++++++++ .../example_configs/riscv-sky130-4kbyte.py | 26 +++++++++++++++++++ 10 files changed, 254 insertions(+) create mode 100644 compiler/example_configs/riscv-freepdk45-8kbyte.py create mode 100644 compiler/example_configs/riscv-scn4m_subm-16kbyte.py create mode 100644 compiler/example_configs/riscv-scn4m_subm-1kbyte.py create mode 100644 compiler/example_configs/riscv-scn4m_subm-2kbyte.py create mode 100644 compiler/example_configs/riscv-scn4m_subm-32kbyte.py create mode 100644 compiler/example_configs/riscv-scn4m_subm-4kbyte.py create mode 100644 compiler/example_configs/riscv-scn4m_subm-8kbyte.py create mode 100644 compiler/example_configs/riscv-sky130-1kbyte.py create mode 100644 compiler/example_configs/riscv-sky130-2kbyte.py create mode 100644 compiler/example_configs/riscv-sky130-4kbyte.py diff --git a/compiler/example_configs/riscv-freepdk45-8kbyte.py b/compiler/example_configs/riscv-freepdk45-8kbyte.py new file mode 100644 index 00000000..b0ebf764 --- /dev/null +++ b/compiler/example_configs/riscv-freepdk45-8kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 2048 +write_size = 8 + +local_array_size = 32 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "freepdk45" +nominal_corners_only = True + +route_supplies = False +check_lvsdrc = False +perimeter_pins = False +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-scn4m_subm-16kbyte.py b/compiler/example_configs/riscv-scn4m_subm-16kbyte.py new file mode 100644 index 00000000..befe49fb --- /dev/null +++ b/compiler/example_configs/riscv-scn4m_subm-16kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 4096 +write_size = 8 + +local_array_size = 32 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = True + +route_supplies = False +check_lvsdrc = False +perimeter_pins = False +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-scn4m_subm-1kbyte.py b/compiler/example_configs/riscv-scn4m_subm-1kbyte.py new file mode 100644 index 00000000..1ab9f5fe --- /dev/null +++ b/compiler/example_configs/riscv-scn4m_subm-1kbyte.py @@ -0,0 +1,24 @@ +word_size = 32 +num_words = 256 +write_size = 8 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = True + +route_supplies = True +check_lvsdrc = True +perimeter_pins = True +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-scn4m_subm-2kbyte.py b/compiler/example_configs/riscv-scn4m_subm-2kbyte.py new file mode 100644 index 00000000..267e31ef --- /dev/null +++ b/compiler/example_configs/riscv-scn4m_subm-2kbyte.py @@ -0,0 +1,24 @@ +word_size = 32 +num_words = 512 +write_size = 8 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = True + +route_supplies = True +check_lvsdrc = True +perimeter_pins = True +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-scn4m_subm-32kbyte.py b/compiler/example_configs/riscv-scn4m_subm-32kbyte.py new file mode 100644 index 00000000..98cb8808 --- /dev/null +++ b/compiler/example_configs/riscv-scn4m_subm-32kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 8192 +write_size = 8 + +local_array_size = 32 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = True + +route_supplies = False +check_lvsdrc = False +perimeter_pins = False +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-scn4m_subm-4kbyte.py b/compiler/example_configs/riscv-scn4m_subm-4kbyte.py new file mode 100644 index 00000000..87cf4a71 --- /dev/null +++ b/compiler/example_configs/riscv-scn4m_subm-4kbyte.py @@ -0,0 +1,24 @@ +word_size = 32 +num_words = 1024 +write_size = 8 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = True + +route_supplies = True +check_lvsdrc = True +perimeter_pins = True +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-scn4m_subm-8kbyte.py b/compiler/example_configs/riscv-scn4m_subm-8kbyte.py new file mode 100644 index 00000000..8b1715ff --- /dev/null +++ b/compiler/example_configs/riscv-scn4m_subm-8kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 2048 +write_size = 8 + +local_array_size = 32 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +nominal_corners_only = True + +route_supplies = False +check_lvsdrc = False +perimeter_pins = False +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-sky130-1kbyte.py b/compiler/example_configs/riscv-sky130-1kbyte.py new file mode 100644 index 00000000..c4a298d2 --- /dev/null +++ b/compiler/example_configs/riscv-sky130-1kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 256 +write_size = 8 + +local_array_size = 16 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "sky130" +nominal_corners_only = True + +route_supplies = True +check_lvsdrc = True +perimeter_pins = True +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-sky130-2kbyte.py b/compiler/example_configs/riscv-sky130-2kbyte.py new file mode 100644 index 00000000..d50d532e --- /dev/null +++ b/compiler/example_configs/riscv-sky130-2kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 512 +write_size = 8 + +local_array_size = 16 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "sky130" +nominal_corners_only = True + +route_supplies = True +check_lvsdrc = True +perimeter_pins = True +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) diff --git a/compiler/example_configs/riscv-sky130-4kbyte.py b/compiler/example_configs/riscv-sky130-4kbyte.py new file mode 100644 index 00000000..2d870e88 --- /dev/null +++ b/compiler/example_configs/riscv-sky130-4kbyte.py @@ -0,0 +1,26 @@ +word_size = 32 +num_words = 1024 +write_size = 8 + +local_array_size = 16 + +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "sky130" +nominal_corners_only = True + +route_supplies = True +check_lvsdrc = True +perimeter_pins = True +#netlist_only = True +#analytical_delay = False +output_path = "macros/sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) +output_name = "sram_1rw1r_{0}_{1}_{2}_{3}".format(word_size, + num_words, + write_size, + tech_name) From 27d921d2db130ff8de90cf8e5366562279681079 Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 6 Oct 2020 16:26:35 -0700 Subject: [PATCH 10/12] Fix run-time bug for duplicate instance check --- compiler/base/hierarchy_layout.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 133ecc9b..e4830226 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -37,6 +37,7 @@ class layout(): self.height = None self.bounding_box = None self.insts = [] # Holds module/cell layout instances + self.inst_names = set() # Set of names to check for duplicates self.objs = [] # Holds all other objects (labels, geometries, etc) self.pin_map = {} # Holds name->pin_layout map for all pins self.visited = [] # List of modules we have already visited @@ -214,9 +215,9 @@ class layout(): # Contacts are not really instances, so skip them if "contact" not in mod.name: # Check that the instance name is unique - for inst in self.insts: - debug.check(name != inst.name, "Duplicate named instance in {0}: {1}".format(self.name, name)) - + debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.name, name)) + + self.inst_names.add(name) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) debug.info(3, "adding instance {}".format(self.insts[-1])) # This is commented out for runtime reasons From c2629edc1b0cfe3cf5570dc50667b0b711ac609f Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 6 Oct 2020 16:27:02 -0700 Subject: [PATCH 11/12] Allow 16-way column mux --- compiler/modules/bank.py | 3 ++ compiler/modules/port_address.py | 4 +- compiler/modules/replica_column.py | 6 ++- compiler/sram/sram.py | 14 +++--- compiler/sram/sram_config.py | 74 +++++++++++++++--------------- 5 files changed, 53 insertions(+), 48 deletions(-) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index 41433e46..a1c6c251 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -533,6 +533,9 @@ class bank(design.design): elif self.col_addr_size == 3: self.column_decoder = factory.create(module_type="hierarchical_predecode3x8", height=self.dff.height) + elif self.col_addr_size == 4: + self.column_decoder = factory.create(module_type="hierarchical_predecode4x16", + height=self.dff.height) else: # No error checking before? debug.error("Invalid column decoder?", -1) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index f7e85afd..8146e7b6 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -79,8 +79,8 @@ class port_address(design.design): self.copy_power_pins(inst, "vdd") self.copy_power_pins(inst, "gnd") - rbl_vdd_pin = self.rbl_driver_inst.get_pin("vdd") - self.add_power_pin("vdd", rbl_vdd_pin.lc()) + for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"): + self.add_power_pin("vdd", rbl_vdd_pin.center()) def route_pins(self): for row in range(self.addr_size): diff --git a/compiler/modules/replica_column.py b/compiler/modules/replica_column.py index dafe51e6..6d084261 100644 --- a/compiler/modules/replica_column.py +++ b/compiler/modules/replica_column.py @@ -221,8 +221,10 @@ class replica_column(bitcell_base_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.append("vdd") - bitcell_pins.append("gnd") + if len(self.edge_cell.get_pins("vdd")) > 0: + bitcell_pins.append("vdd") + if len(self.edge_cell.get_pins("gnd")) > 0: + bitcell_pins.append("gnd") return bitcell_pins diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 992919f0..63d971f3 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -79,13 +79,6 @@ class sram(): """ Save all the output files while reporting time to do it as well. """ if not OPTS.netlist_only: - # Create a LEF physical model - start_time = datetime.datetime.now() - lefname = OPTS.output_path + self.s.name + ".lef" - debug.print_raw("LEF: Writing to {0}".format(lefname)) - self.lef_write(lefname) - print_time("LEF", datetime.datetime.now(), start_time) - # Write the layout start_time = datetime.datetime.now() gdsname = OPTS.output_path + self.s.name + ".gds" @@ -93,6 +86,13 @@ class sram(): self.gds_write(gdsname) print_time("GDS", datetime.datetime.now(), start_time) + # Create a LEF physical model + start_time = datetime.datetime.now() + lefname = OPTS.output_path + self.s.name + ".lef" + debug.print_raw("LEF: Writing to {0}".format(lefname)) + self.lef_write(lefname) + print_time("LEF", datetime.datetime.now(), start_time) + # Save the spice file start_time = datetime.datetime.now() spname = OPTS.output_path + self.s.name + ".sp" diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index fa70d730..573e7514 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -6,15 +6,15 @@ # All rights reserved. # import debug -from math import log,sqrt,ceil -from importlib import reload +from math import log, sqrt, ceil from globals import OPTS from sram_factory import factory + class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): + def __init__(self, word_size, num_words, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): self.word_size = word_size self.num_words = num_words self.write_size = write_size @@ -25,7 +25,7 @@ class sram_config: # This will get over-written when we determine the organization self.words_per_row = words_per_row - self.compute_sizes() + self.compute_sizes() def set_local_config(self, module): """ Copy all of the member variables to the given module for convenience """ @@ -34,31 +34,31 @@ class sram_config: # Copy all the variables to the local module for member in members: - setattr(module,member,getattr(self,member)) + setattr(module, member, getattr(self, member)) def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" bitcell = factory.create(module_type="bitcell") - - debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") + debug.check(self.num_banks in [1, 2, 4], + "Valid number of banks are 1 , 2 and 4.") - self.num_words_per_bank = self.num_words/self.num_banks - self.num_bits_per_bank = self.word_size*self.num_words_per_bank + self.num_words_per_bank = self.num_words / self.num_banks + self.num_bits_per_bank = self.word_size * self.num_words_per_bank # If this was hard coded, don't dynamically compute it! if not self.words_per_row: # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) - self.bank_area = bitcell.width*bitcell.height*self.num_bits_per_bank + self.bank_area = bitcell.width * bitcell.height * self.num_bits_per_bank self.bank_side_length = sqrt(self.bank_area) # Estimate the words per row given the height of the bitcell and the square side length - self.tentative_num_cols = int(self.bank_side_length/bitcell.width) + self.tentative_num_cols = int(self.bank_side_length / bitcell.width) self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) # Estimate the number of rows given the tentative words per row - self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) + self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row * self.word_size) self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) self.recompute_sizes() @@ -70,57 +70,57 @@ class sram_config: SRAM for testing. """ - debug.info(1,"Recomputing with words per row: {}".format(self.words_per_row)) + debug.info(1, "Recomputing with words per row: {}".format(self.words_per_row)) # If the banks changed - self.num_words_per_bank = self.num_words/self.num_banks - self.num_bits_per_bank = self.word_size*self.num_words_per_bank + self.num_words_per_bank = self.num_words / self.num_banks + self.num_bits_per_bank = self.word_size * self.num_words_per_bank # Fix the number of columns and rows - self.num_cols = int(self.words_per_row*self.word_size) - self.num_rows_temp = int(self.num_words_per_bank/self.words_per_row) + self.num_cols = int(self.words_per_row * self.word_size) + self.num_rows_temp = int(self.num_words_per_bank / self.words_per_row) self.num_rows = self.num_rows_temp + self.num_spare_rows - debug.info(1,"Rows: {} Cols: {}".format(self.num_rows_temp,self.num_cols)) + debug.info(1, "Rows: {} Cols: {}".format(self.num_rows_temp, self.num_cols)) # Compute the address and bank sizes self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) - debug.info(1,"Row addr size: {}".format(self.row_addr_size) + debug.info(1, "Row addr size: {}".format(self.row_addr_size) + " Col addr size: {}".format(self.col_addr_size) + " Bank addr size: {}".format(self.bank_addr_size)) - - def estimate_words_per_row(self,tentative_num_cols, word_size): + def estimate_words_per_row(self, tentative_num_cols, word_size): """ This provides a heuristic rounded estimate for the number of words per row. """ + tentative_column_ways = tentative_num_cols / word_size + column_mux_sizes = [1, 2, 4, 8, 16] + # If we are double, we may want a larger column mux + if tentative_column_ways > 2 * column_mux_sizes[-1]: + debug.warning("Extremely large number of columns for 16-way maximum column mux.") - if tentative_num_cols < 1.5*word_size: - return 1 - elif tentative_num_cols < 3*word_size: - return 2 - elif tentative_num_cols < 6*word_size: - return 4 - else: - if tentative_num_cols > 16*word_size: - debug.warning("Reaching column mux size limit. Consider increasing above 8-way.") - return 8 + closest_way = min(column_mux_sizes, key=lambda x: abs(x - tentative_column_ways)) - def amend_words_per_row(self,tentative_num_rows, words_per_row): + return closest_way + + def amend_words_per_row(self, tentative_num_rows, words_per_row): """ This picks the number of words per row more accurately by limiting it to a minimum and maximum. """ # Recompute the words per row given a hard max if(not OPTS.is_unit_test and tentative_num_rows > 512): - debug.check(tentative_num_rows*words_per_row <= 2048, "Number of words exceeds 2048") - return int(words_per_row*tentative_num_rows/512) + debug.check(tentative_num_rows * words_per_row <= 4096, + "Number of words exceeds 2048") + return int(words_per_row * tentative_num_rows / 512) + # Recompute the words per row given a hard min - if(not OPTS.is_unit_test and tentative_num_rows < 16): - debug.check(tentative_num_rows*words_per_row >= 16, "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) - return int(words_per_row*tentative_num_rows/16) + if (not OPTS.is_unit_test and tentative_num_rows < 16): + debug.check(tentative_num_rows * words_per_row >= 16, + "Minimum number of rows is 16, but given {0}".format(tentative_num_rows)) + return int(words_per_row * tentative_num_rows / 16) return words_per_row From 483f6b187cd0723d196c4ef651013570cdba367a Mon Sep 17 00:00:00 2001 From: mrg Date: Tue, 6 Oct 2020 16:47:32 -0700 Subject: [PATCH 12/12] RBL driver supply location differs for sky130 and other techs --- compiler/modules/port_address.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index 8146e7b6..e416eb43 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -80,8 +80,11 @@ class port_address(design.design): self.copy_power_pins(inst, "gnd") for rbl_vdd_pin in self.rbl_driver_inst.get_pins("vdd"): - self.add_power_pin("vdd", rbl_vdd_pin.center()) - + if OPTS.tech_name == "sky130": + self.add_power_pin("vdd", rbl_vdd_pin.center()) + else: + self.add_power_pin("vdd", rbl_vdd_pin.lc()) + def route_pins(self): for row in range(self.addr_size): decoder_name = "addr_{}".format(row)