diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 27c341aa..27431cb2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ before_script: - . /home/gitlab-runner/setup-paths.sh - export OPENRAM_HOME="`pwd`/compiler" - - export OPENRAM_TECH="`pwd`/technology" + - export OPENRAM_TECH="`pwd`/technology:/home/PDKs/skywater-tech" stages: - test @@ -25,6 +25,15 @@ scn4m_subm: - .coverage.* expire_in: 1 week +# s8: +# stage: test +# script: +# - coverage run -p $OPENRAM_HOME/tests/regress.py -t s8 +# artifacts: +# paths: +# - .coverage.* +# expire_in: 1 week + coverage: stage: coverage script: diff --git a/compiler/base/contact.py b/compiler/base/contact.py index f4fda552..14dbf76e 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -53,6 +53,10 @@ class contact(hierarchy_design.hierarchy_design): first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V" second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V" self.directions = (first_dir, second_dir) + # Preferred directions + elif directions == "pref": + self.directions = (tech.preferred_directions[layer_stack[0]], + tech.preferred_directions[layer_stack[2]]) # User directions elif directions: self.directions = directions @@ -149,7 +153,7 @@ class contact(hierarchy_design.hierarchy_design): self.first_layer_vertical_enclosure = max(self.first_layer_enclosure, (self.first_layer_minwidth - self.contact_array_height) / 2) else: - debug.error("Invalid first layer direction.", -1) + debug.error("Invalid first layer direction: ".format(self.directions[0]), -1) # In some technologies, the minimum width may be larger # than the overlap requirement around the via, so @@ -165,7 +169,7 @@ class contact(hierarchy_design.hierarchy_design): self.second_layer_vertical_enclosure = max(self.second_layer_enclosure, (self.second_layer_minwidth - self.contact_array_width) / 2) else: - debug.error("Invalid second layer direction.", -1) + debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1) def create_contact_array(self): """ Create the contact array at the origin""" diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 96da3d99..32af7ee9 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -86,8 +86,14 @@ class geometry: elif mirror == "XY": ll = ll.scale(-1, -1) ur = ur.scale(-1, -1) + elif mirror == "" or mirror == "R0": + pass + else: + debug.error("Invalid mirroring: {}".format(mirror), -1) - if rotate == 90: + if rotate == 0: + pass + elif rotate == 90: ll = ll.rotate_scale(-1, 1) ur = ur.rotate_scale(-1, 1) elif rotate == 180: @@ -96,6 +102,8 @@ class geometry: elif rotate == 270: ll = ll.rotate_scale(1, -1) ur = ur.rotate_scale(1, -1) + else: + debug.error("Invalid rotation: {}".format(rotate), -1) self.boundary = [offset + ll, offset + ur] self.normalize() @@ -139,6 +147,10 @@ class geometry: def cy(self): """ Return the center y """ return 0.5 * (self.boundary[0].y + self.boundary[1].y) + + def center(self): + """ Return the center coordinate """ + return vector(self.cx(), self.cy()) class instance(geometry): diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 722ac2f1..1a0d7417 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -244,25 +244,27 @@ class layout(): height)) return self.objs[-1] - def add_segment_center(self, layer, start, end): + def add_segment_center(self, layer, start, end, width=None): """ Add a min-width rectanglular segment using center line on the start to end point """ - minwidth_layer = drc["minwidth_{}".format(layer)] + if not width: + width = drc["minwidth_{}".format(layer)] + if start.x != end.x and start.y != end.y: debug.error("Nonrectilinear center rect!", -1) elif start.x != end.x: - offset = vector(0, 0.5 * minwidth_layer) + offset = vector(0, 0.5 * width) return self.add_rect(layer, start - offset, end.x - start.x, - minwidth_layer) + width) else: - offset = vector(0.5 * minwidth_layer, 0) + offset = vector(0.5 * width, 0) return self.add_rect(layer, start - offset, - minwidth_layer, + width, end.y - start.y) def get_pin(self, text): @@ -322,7 +324,7 @@ class layout(): for pin_name in self.pin_map.keys(): self.copy_layout_pin(instance, pin_name, prefix + pin_name) - def add_layout_pin_segment_center(self, text, layer, start, end): + def add_layout_pin_segment_center(self, text, layer, start, end, width=None): """ Creates a path like pin with center-line convention """ @@ -331,27 +333,27 @@ class layout(): self.gds_write(file_name) debug.error("Cannot have a non-manhatten layout pin: {}".format(file_name), -1) - minwidth_layer = drc["minwidth_{}".format(layer)] + if not width: + layer_width = drc["minwidth_{}".format(layer)] + else: + layer_width = width # one of these will be zero - width = max(start.x, end.x) - min(start.x, end.x) - height = max(start.y, end.y) - min(start.y, end.y) + bbox_width = max(start.x, end.x) - min(start.x, end.x) + bbox_height = max(start.y, end.y) - min(start.y, end.y) ll_offset = vector(min(start.x, end.x), min(start.y, end.y)) # Shift it down 1/2 a width in the 0 dimension - if height == 0: - ll_offset -= vector(0, 0.5 * minwidth_layer) - if width == 0: - ll_offset -= vector(0.5 * minwidth_layer, 0) - # This makes sure it is long enough, but also it is not 0 width! - height = max(minwidth_layer, height) - width = max(minwidth_layer, width) + if bbox_height == 0: + ll_offset -= vector(0, 0.5 * layer_width) + if bbox_width == 0: + ll_offset -= vector(0.5 * layer_width, 0) - return self.add_layout_pin(text, - layer, - ll_offset, - width, - height) + return self.add_layout_pin(text=text, + layer=layer, + offset=ll_offset, + width=bbox_width, + height=bbox_height) def add_layout_pin_rect_center(self, text, layer, offset, width=None, height=None): """ Creates a path like pin with center-line convention """ @@ -692,6 +694,8 @@ class layout(): boundary_layer = "stdc" boundary = [self.find_lowest_coords(), self.find_highest_coords()] + debug.check(boundary[0] and boundary[1], "No shapes to make a boundary.") + height = boundary[1][1] - boundary[0][1] width = boundary[1][0] - boundary[0][0] (layer_number, layer_purpose) = techlayer[boundary_layer] @@ -1335,16 +1339,16 @@ class layout(): which vias are needed. """ - via = self.add_via_stack_center(from_layer=start_layer, - to_layer=self.pwr_grid_layer, - size=size, - offset=loc, - directions=directions) if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc) else: + via = self.add_via_stack_center(from_layer=start_layer, + to_layer=self.pwr_grid_layer, + size=size, + offset=loc, + directions=directions) # Hack for min area if OPTS.tech_name == "s8": width = round_to_grid(sqrt(drc["minarea_m3"])) diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 8091af63..00d7ad44 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -208,6 +208,7 @@ class spice(): # parses line into ports and remove subckt self.pins = subckt_line.split(" ")[2:] else: + debug.info(4, "no spfile {0}".format(self.sp_file)) self.spice = [] # We don't define self.lvs and will use self.spice if dynamically created diff --git a/compiler/bitcells/bitcell.py b/compiler/bitcells/bitcell.py index b0e79208..4ed2d053 100644 --- a/compiler/bitcells/bitcell.py +++ b/compiler/bitcells/bitcell.py @@ -7,7 +7,7 @@ # import debug import utils -from tech import GDS, layer, parameter +from tech import GDS, layer from tech import cell_properties as props import bitcell_base diff --git a/compiler/modules/dff.py b/compiler/custom/dff.py similarity index 95% rename from compiler/modules/dff.py rename to compiler/custom/dff.py index e319459e..c8fdb4b0 100644 --- a/compiler/modules/dff.py +++ b/compiler/custom/dff.py @@ -61,7 +61,7 @@ class dff(design.design): #Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width. return parameter["dff_clk_cin"] - def build_graph(self, graph, inst_name, port_nets): + 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) + self.add_graph_edges(graph, port_nets) diff --git a/compiler/custom/inv_dec.py b/compiler/custom/inv_dec.py new file mode 100644 index 00000000..80fdb74e --- /dev/null +++ b/compiler/custom/inv_dec.py @@ -0,0 +1,80 @@ +# 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 design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils +import debug + + +class inv_dec(design.design): + """ + INV for address decoders. + """ + + pin_names = ["A", "Z", "vdd", "gnd"] + type_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("inv_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "inv_dec", GDS["unit"]) + + def __init__(self, name="inv_dec", height=None): + design.design.__init__(self, name) + + self.width = inv_dec.width + self.height = inv_dec.height + self.pin_map = inv_dec.pin_map + self.add_pin_types(self.type_list) + + 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["inv_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"]) + + return transition_prob * (c_load + c_para) + + def input_load(self): + """ + Return the capacitance of the gate connection in generic capacitive + units relative to the minimum width of a transistor + """ + 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 = 1 + 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/custom/nand2_dec.py b/compiler/custom/nand2_dec.py new file mode 100644 index 00000000..30a018a1 --- /dev/null +++ b/compiler/custom/nand2_dec.py @@ -0,0 +1,77 @@ +# 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 design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand2_dec(design.design): + """ + 2-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand2_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand2_dec", GDS["unit"]) + + def __init__(self, name="nand2_dec", height=None): + design.design.__init__(self, name) + + self.width = nand2_dec.width + self.height = nand2_dec.height + self.pin_map = nand2_dec.pin_map + self.add_pin_types(self.type_list) + + 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["nand2_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.1875 + 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 = 2 + 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/custom/nand3_dec.py b/compiler/custom/nand3_dec.py new file mode 100644 index 00000000..d700062c --- /dev/null +++ b/compiler/custom/nand3_dec.py @@ -0,0 +1,77 @@ +# 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 design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand3_dec(design.design): + """ + 3-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "C", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand3_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand3_dec", GDS["unit"]) + + def __init__(self, name="nand3_dec", height=None): + design.design.__init__(self, name) + + self.width = nand3_dec.width + self.height = nand3_dec.height + self.pin_map = nand3_dec.pin_map + self.add_pin_types(self.type_list) + + 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["nand3_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.1875 + 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 = 2 + 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/custom/nand4_dec.py b/compiler/custom/nand4_dec.py new file mode 100644 index 00000000..5c2bb882 --- /dev/null +++ b/compiler/custom/nand4_dec.py @@ -0,0 +1,77 @@ +# 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 design +from tech import GDS, layer, spice, parameter +import logical_effort +import utils + + +class nand4_dec(design.design): + """ + 2-input NAND decoder for address decoders. + """ + + pin_names = ["A", "B", "C", "D", "Z", "vdd", "gnd"] + type_list = ["INPUT", "INPUT", "INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] + + (width, height) = utils.get_libcell_size("nand4_dec", + GDS["unit"], + layer["boundary"]) + pin_map = utils.get_libcell_pins(pin_names, "nand4_dec", GDS["unit"]) + + def __init__(self, name="nand4_dec", height=None): + design.design.__init__(self, name) + + self.width = nand4_dec.width + self.height = nand4_dec.height + self.pin_map = nand4_dec.pin_map + self.add_pin_types(self.type_list) + + 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.1875 + 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 = 2 + 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/custom/pand2_dec.py b/compiler/custom/pand2_dec.py new file mode 100644 index 00000000..0db490ff --- /dev/null +++ b/compiler/custom/pand2_dec.py @@ -0,0 +1,138 @@ +# 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 +from globals import OPTS + + +class pand2_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand2_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand2_dec") + else: + self.nand = factory.create(module_type="nand2_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + height=self.height, + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + 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("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand2_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand2_dec_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)) + + # 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. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + 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 OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.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"]: + 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 get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/custom/pand3_dec.py b/compiler/custom/pand3_dec.py new file mode 100644 index 00000000..3ec1eb45 --- /dev/null +++ b/compiler/custom/pand3_dec.py @@ -0,0 +1,149 @@ +# 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 +from globals import OPTS + + +class pand3_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand3_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand3_dec") + else: + self.nand = factory.create(module_type="nand3_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + height=self.height, + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + 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("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="pand3_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand3_dec_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)) + + # 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. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + 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 OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.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 + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/custom/pand4_dec.py b/compiler/custom/pand4_dec.py new file mode 100644 index 00000000..ee65a349 --- /dev/null +++ b/compiler/custom/pand4_dec.py @@ -0,0 +1,149 @@ +# 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 +from globals import OPTS + + +class pand4_dec(pgate.pgate): + """ + This is an AND with configurable drive strength. + """ + def __init__(self, name, size=1, height=None, add_wells=True): + debug.info(1, "Creating pand4_dec {}".format(name)) + self.add_comment("size: {}".format(size)) + + self.size = size + + pgate.pgate.__init__(self, name, height, add_wells) + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand4_dec") + else: + self.nand = factory.create(module_type="nand4_dec", + height=self.height) + + self.inv = factory.create(module_type="inv_dec", + size=self.size) + + self.add_mod(self.nand) + self.add_mod(self.inv) + + def create_layout(self): + 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_dec_nand", + mod=self.nand) + self.connect_inst(["A", "B", "C", "D", "zb_int", "vdd", "gnd"]) + + self.inv_inst = self.add_inst(name="pand4_dec_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)) + + # 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. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.inv_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + self.add_layout_pin_rect_center(text="vdd", + layer=self.route_layer, + offset=vector(0.5 * self.width, self.height), + 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 OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.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 + + def get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.inv.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + last_stage_is_rise = stage1.is_rise + + stage2 = self.inv.get_stage_effort(external_cout, last_stage_is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/modules/tri_gate.py b/compiler/custom/tri_gate.py similarity index 100% rename from compiler/modules/tri_gate.py rename to compiler/custom/tri_gate.py diff --git a/compiler/modules/write_driver.py b/compiler/custom/write_driver.py similarity index 100% rename from compiler/modules/write_driver.py rename to compiler/custom/write_driver.py diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index c1593932..3cb5d35c 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -12,7 +12,6 @@ from sram_factory import factory from vector import vector from globals import OPTS from errors import drc_error -from tech import cell_properties, layer class hierarchical_decoder(design.design): @@ -28,12 +27,8 @@ class hierarchical_decoder(design.design): self.pre3x8_inst = [] b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height - + 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) @@ -41,41 +36,6 @@ class hierarchical_decoder(design.design): self.create_netlist() if not OPTS.netlist_only: self.create_layout() - - def find_decoder_height(self): - """ - Dead code. This would dynamically determine the bitcell multiple, - but I just decided to hard code it in the tech file if it is not 1 - because a DRC tool would be required even to run in front-end mode. - """ - b = factory.create(module_type="bitcell") - - # Old behavior - if OPTS.netlist_only: - return (b.height, 1) - - # Search for the smallest multiple that works - cell_multiple = 1 - while cell_multiple < 5: - cell_height = cell_multiple * b.height - # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height)) - try: - and3 = factory.create(module_type="pand3", - height=cell_height) - except drc_error: - # debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height)) - pass - else: - (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) - total_errors = drc_errors + lvs_errors - if total_errors == 0: - debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple)) - return (cell_height, cell_multiple) - - cell_multiple += 1 - - else: - debug.error("Couldn't find a valid decoder height multiple.", -1) def create_netlist(self): self.add_modules() @@ -97,15 +57,22 @@ class hierarchical_decoder(design.design): self.DRC_LVS() def add_modules(self): - self.inv = factory.create(module_type="pinv", - height=self.cell_height) - self.add_mod(self.inv) - self.and2 = factory.create(module_type="pand2", - height=self.cell_height) + if OPTS.tech_name == "s8": + self.and2 = factory.create(module_type="pand2_dec") + else: + self.and2 = factory.create(module_type="pand2_dec", + height=self.cell_height) self.add_mod(self.and2) - self.and3 = factory.create(module_type="pand3", - height=self.cell_height) + if OPTS.tech_name == "s8": + self.and3 = factory.create(module_type="pand3_dec") + else: + self.and3 = factory.create(module_type="pand3_dec", + height=self.cell_height) + self.add_mod(self.and3) + # TBD + # self.and4 = factory.create(module_type="pand4_dec") + # self.add_mod(self.and4) self.add_decoders() @@ -176,56 +143,63 @@ class hierarchical_decoder(design.design): -1) # Calculates height and width of pre-decoder, - if self.no_of_pre3x8 > 0: + # 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_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 - # We may have more than one bitcell per decoder row - self.num_rows = math.ceil(self.num_outputs / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows) - # We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row - if self.decoders_per_row == 1: - self.decoder_bus_pitch = self.m2_pitch - elif self.decoders_per_row == 2: - self.decoder_bus_pitch = self.m3_pitch + # How much space between each predecoder + self.predecoder_spacing = 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 + + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.bus_layer = "m1" + self.bus_directions = "nonpref" + self.bus_pitch = self.m1_pitch + self.bus_space = self.m2_space + self.input_layer = "m2" + self.output_layer = "li" + self.output_layer_pitch = self.li_pitch else: - debug.error("Insufficient layers for multi-bit height decoder.", -1) + self.bus_layer = "m2" + self.bus_directions = "pref" + self.bus_pitch = self.m2_pitch + self.bus_space = self.m2_space + # These two layers being the same requires a special jog + # to ensure to conflicts with the output layers + self.input_layer = "m1" + self.output_layer = "m3" + self.output_layer_pitch = self.m3_pitch + + # Two extra pitches between modules on left and right + self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch + self.row_decoder_height = self.and2.height * self.num_outputs + # Extra bus space for supply contacts + self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space # Calculates height and width of row-decoder - if (self.num_inputs == 4 or self.num_inputs == 5): - nand_width = self.and2.width - nand_inputs = 2 - else: - nand_width = self.and3.width - nand_inputs = 3 - self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1) - self.row_decoder_height = self.inv.height * self.num_rows - - decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch - # print(self.decoders_per_row, nand_inputs) - # print(decoder_input_wire_height, self.cell_height) - if decoder_input_wire_height > self.cell_height: - debug.warning("Cannot fit multi-bit decoder routes per row.") - # debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.") - - self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch - # Calculates height and width of hierarchical decoder # Add extra pitch for good measure - self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch - self.width = self.input_routing_width + self.predecoder_width \ + self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space + if (self.num_inputs == 4 or self.num_inputs == 5): + self.nand_width = self.and2.width + else: + self.nand_width = self.and3.width + + self.width = self.input_routing_width \ + + self.predecoder_width \ + self.internal_routing_width \ - + self.decoders_per_row * nand_width + self.inv.width - + + self.nand_width \ + + self.m1_space + def route_inputs(self): """ Create input bus for the predecoders """ - # inputs should be as high as the decoders - input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height - # Find the left-most predecoder min_x = 0 if self.no_of_pre2x4 > 0: @@ -235,10 +209,10 @@ class hierarchical_decoder(design.design): input_offset=vector(min_x - self.input_routing_width, 0) input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] - self.input_bus = self.create_vertical_pin_bus(layer="m2", + self.input_bus = self.create_vertical_pin_bus(layer=self.bus_layer, offset=input_offset, names=input_bus_names, - length=input_height) + length=self.predecoder_height) self.route_input_to_predecodes() @@ -253,9 +227,7 @@ class hierarchical_decoder(design.design): in_name = "in_{}".format(i) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) - # To prevent conflicts, we will offset each input connect so - # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch)) + 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) @@ -269,9 +241,7 @@ class hierarchical_decoder(design.design): in_name = "in_{}".format(i) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) - # To prevent conflicts, we will offset each input connect so - # that it aligns with the vdd/gnd rails - decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch)) + 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) @@ -282,13 +252,14 @@ class hierarchical_decoder(design.design): vertical M2 coordinate to the predecode inputs """ - self.add_via_stack_center(from_layer="m2", - to_layer="m3", + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, offset=input_offset) - self.add_via_stack_center(from_layer="m2", - to_layer="m3", - offset=output_offset) - self.add_path("m3", [input_offset, output_offset]) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, + offset=output_offset, + directions=self.bus_directions) + self.add_path(self.input_layer, [input_offset, output_offset]) def add_pins(self): """ Add the module pins """ @@ -363,19 +334,19 @@ class hierarchical_decoder(design.design): if (self.num_inputs == 2): base = vector(-self.pre2_4.width, 0) else: - base= vector(-self.pre2_4.width, num * self.pre2_4.height) + base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing)) - self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0)) + self.pre2x4_inst[num].place(base) def place_pre3x8(self, num): """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ if (self.num_inputs == 3): offset = vector(-self.pre_3_8.width, 0) else: - height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height + height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing) offset = vector(-self.pre3_8.width, height) - self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0)) + self.pre3x8_inst[num].place(offset) def create_row_decoder(self): """ Create the row-decoder by placing AND2/AND3 and Inverters @@ -431,7 +402,6 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): self.place_decoder_and_array() - def place_decoder_and_array(self): """ Add a column of AND gates for final decode. @@ -452,9 +422,7 @@ class hierarchical_decoder(design.design): Add a column of AND gates for the decoder above the predecoders. """ - for inst_index in range(self.num_outputs): - row = math.floor(inst_index / self.decoders_per_row) - dec = inst_index % self.decoders_per_row + for row in range(self.num_outputs): if ((row % 2) == 0): y_off = and_mod.height * row mirror = "R0" @@ -462,32 +430,16 @@ class hierarchical_decoder(design.design): y_off = and_mod.height * (row + 1) mirror = "MX" - x_off = self.internal_routing_width + dec * and_mod.width - self.and_inst[inst_index].place(offset=vector(x_off, y_off), - mirror=mirror) + x_off = self.internal_routing_width + self.and_inst[row].place(offset=vector(x_off, y_off), + mirror=mirror) def route_outputs(self): """ Add the pins. """ - max_xoffset = max(x.rx() for x in self.and_inst) - - for output_index in range(self.num_outputs): - row_remainder = (output_index % self.decoders_per_row) - - and_inst = self.and_inst[output_index] - z_pin = and_inst.get_pin("Z") - if row_remainder == 0 and self.decoders_per_row > 1: - layer = "m3" - self.add_via_stack_center(from_layer=z_pin.layer, - to_layer="m3", - offset=z_pin.center()) - else: - layer = z_pin.layer - - self.add_layout_pin_segment_center(text="decode_{0}".format(output_index), - layer=layer, - start=z_pin.center(), - end=vector(max_xoffset, z_pin.cy())) + for row in range(self.num_outputs): + and_inst = self.and_inst[row] + self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row)) def route_decoder_bus(self): """ @@ -498,9 +450,9 @@ class hierarchical_decoder(design.design): if (self.num_inputs >= 4): # This leaves an offset for the predecoder output jogs input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] - self.predecode_bus = self.create_vertical_pin_bus(layer="m2", - pitch=self.decoder_bus_pitch, - offset=vector(0, 0), + self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer, + pitch=self.bus_pitch, + offset=vector(self.bus_pitch, 0), names=input_bus_names, length=self.height) @@ -518,8 +470,9 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(pre_num * 4 + i) out_name = "out_{}".format(i) pin = self.pre2x4_inst[pre_num].get_pin(out_name) - x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch - self.route_predecode_bus_inputs(predecode_name, pin, x_offset) + x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch + y_offset = self.pre2x4_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_pre3x8): @@ -527,8 +480,9 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) out_name = "out_{}".format(i) pin = self.pre3x8_inst[pre_num].get_pin(out_name) - x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch - self.route_predecode_bus_inputs(predecode_name, pin, x_offset) + 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) def route_bus_to_decoder(self): """ @@ -542,12 +496,6 @@ class hierarchical_decoder(design.design): and the 128th AND3 is connected to [3,7,15] """ output_index = 0 - - if "li" in layer: - self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]] - else: - self.decoder_layers = [self.m2_stack[::-1]] - debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.") if (self.num_inputs == 4 or self.num_inputs == 5): for index_B in self.predec_groups[1]: @@ -557,13 +505,11 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), - output_index, - 0) + output_index) predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - output_index, - 1) + output_index) output_index = output_index + 1 elif (self.num_inputs > 5): @@ -575,18 +521,15 @@ class hierarchical_decoder(design.design): predecode_name = "predecode_{}".format(index_A) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("A"), - output_index, - 0) + output_index) predecode_name = "predecode_{}".format(index_B) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("B"), - output_index, - 1) + output_index) predecode_name = "predecode_{}".format(index_C) self.route_predecode_bus_outputs(predecode_name, self.and_inst[output_index].get_pin("C"), - output_index, - 2) + output_index) output_index = output_index + 1 def route_vdd_gnd(self): @@ -594,90 +537,90 @@ class hierarchical_decoder(design.design): Add a pin for each row of vdd/gnd which are must-connects next level up. """ + + if OPTS.tech_name == "s8": + for n in ["vdd", "gnd"]: + pins = self.and_inst[0].get_pins(n) + for pin in pins: + self.add_rect(layer=pin.layer, + offset=pin.ll() + vector(0, self.bus_space), + width=pin.width(), + height=self.height - 2 * self.bus_space) - # The vias will be placed at the right of the cells. - xoffset = max(x.rx() for x in self.and_inst) - for num in range(0, self.num_outputs): - # Only add the power pin for the 1st in each row - if num % self.decoders_per_row: - continue - - for pin_name in ["vdd", "gnd"]: - # The nand and inv are the same height rows... - supply_pin = self.and_inst[num].get_pin(pin_name) - pin_pos = vector(xoffset, supply_pin.cy()) - self.add_path(supply_pin.layer, - [supply_pin.lc(), vector(xoffset, supply_pin.cy())]) - self.add_power_pin(name=pin_name, - loc=pin_pos, - start_layer=supply_pin.layer) - - # Copy the pins from the predecoders - for pre in self.pre2x4_inst + self.pre3x8_inst: - self.copy_layout_pin(pre, "vdd") - self.copy_layout_pin(pre, "gnd") + # This adds power vias at the top of each cell + # (except the last to keep them inside the boundary) + for i in self.and_inst[:-1]: + pins = i.get_pins(n) + for pin in pins: + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + + for i in self.pre2x4_inst + self.pre3x8_inst: + self.copy_layout_pin(i, n) + else: + # The vias will be placed at the right of the cells. + xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space + for row in range(0, self.num_outputs): + for pin_name in ["vdd", "gnd"]: + # The nand and inv are the same height rows... + supply_pin = self.and_inst[row].get_pin(pin_name) + pin_pos = vector(xoffset, supply_pin.cy()) + self.add_power_pin(name=pin_name, + loc=pin_pos, + start_layer=supply_pin.layer) + + # Copy the pins from the predecoders + for pre in self.pre2x4_inst + self.pre3x8_inst: + for pin_name in ["vdd", "gnd"]: + self.copy_layout_pin(pre, pin_name) - def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index): + def route_predecode_bus_outputs(self, rail_name, pin, row): """ Connect the routing rail to the given metal1 pin using a routing track at the given y_offset """ - row_index = math.floor(output_index / self.decoders_per_row) - row_remainder = (output_index % self.decoders_per_row) - row_offset = row_index * self.and_inst[0].height - pin_pos = pin.center() - - # y_offset is the same for both the M2 and M4 routes so that the rail - # contacts align and don't cause problems - if pin_index == 0: - # Bottom pitch - y_offset = row_offset - elif pin_index == 1: - # One pitch from top - y_offset = row_offset + self.and_inst[0].height - self.m3_pitch - elif pin_index == 2: - # One pitch from bottom - y_offset = row_offset + self.m3_pitch - else: - debug.error("Invalid decoder pitch.") - - rail_pos = vector(self.predecode_bus[rail_name].x, y_offset) - mid_pos = vector(pin_pos.x, rail_pos.y) - self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos]) + rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + self.add_path(self.input_layer, [rail_pos, pin_pos]) - self.add_via_stack_center(from_layer="m2", - to_layer=self.decoder_layers[row_remainder][0], - offset=rail_pos) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.input_layer, + offset=rail_pos, + directions=self.bus_directions) self.add_via_stack_center(from_layer=pin.layer, - to_layer=self.decoder_layers[row_remainder][2], + to_layer=self.input_layer, offset=pin_pos, directions=("H", "H")) - def route_predecode_bus_inputs(self, rail_name, pin, x_offset): + def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset): """ Connect the routing rail to the given metal1 pin using a jog to the right of the cell at the given x_offset. """ # This routes the pin up to the rail, basically, to avoid conflicts. # It would be fixed with a channel router. - # pin_pos = pin.center() - # mid_point1 = vector(x_offset, pin_pos.y) - # mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2) - # rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) - # self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos]) + pin_pos = pin.rc() + mid_point1 = vector(x_offset, pin_pos.y) + mid_point2 = vector(x_offset, y_offset) + rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) + self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos]) - pin_pos = pin.center() - rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) - self.add_path("m1", [pin_pos, rail_pos]) + # pin_pos = pin.center() + # rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) + # self.add_path(self.output_layer, [pin_pos, rail_pos]) self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.output_layer, offset=pin_pos) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=rail_pos) + self.add_via_stack_center(from_layer=self.bus_layer, + to_layer=self.output_layer, + offset=rail_pos, + directions=self.bus_directions) def input_load(self): if self.determine_predecodes(self.num_inputs)[1]==0: diff --git a/compiler/modules/hierarchical_predecode.py b/compiler/modules/hierarchical_predecode.py index 9a658546..8244028e 100644 --- a/compiler/modules/hierarchical_predecode.py +++ b/compiler/modules/hierarchical_predecode.py @@ -8,29 +8,24 @@ import debug import design import math -import contact from vector import vector from sram_factory import factory -from tech import cell_properties +from globals import OPTS class hierarchical_predecode(design.design): """ - Pre 2x4 and 3x8 decoder shared code. + Pre 2x4 and 3x8 and TBD 4x16 decoder shared code. """ def __init__(self, name, input_number, height=None): self.number_of_inputs = input_number if not height: b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height + self.cell_height = b.height else: self.cell_height = height - + self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) design.design.__init__(self, name) @@ -44,34 +39,73 @@ class hierarchical_predecode(design.design): def add_modules(self): """ Add the INV and AND gate modules """ - - self.inv = factory.create(module_type="pinv", - height=self.cell_height) - self.add_mod(self.inv) - - self.add_and(self.number_of_inputs) - self.add_mod(self.and_mod) - def add_and(self, inputs): - """ Create the NAND for the predecode input stage """ - if inputs==2: - self.and_mod = factory.create(module_type="pand2", + # FIXME: Default parms are required for hard cells for now. + if self.number_of_inputs == 2: + self.and_mod = factory.create(module_type="pand2_dec", height=self.cell_height) - elif inputs==3: - self.and_mod = factory.create(module_type="pand3", + elif self.number_of_inputs == 3: + self.and_mod = factory.create(module_type="pand3_dec", + height=self.cell_height) + elif self.number_of_inputs == 4: + self.and_mod = factory.create(module_type="pand4_dec", height=self.cell_height) else: - debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) - + debug.error("Invalid number of predecode inputs: {}".format(self.number_of_inputs), -1) + self.add_mod(self.and_mod) + + # This uses the pinv_dec parameterized cell + self.inv = factory.create(module_type="inv_dec", + height=self.cell_height, + size=1) + self.add_mod(self.inv) + + + def create_layout(self): + """ The general organization is from left to right: + 1) a set of M2 rails for input signals + 2) a set of inverters to invert input signals + 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs + 4) a set of AND gates for inversion + """ + self.setup_layout_constraints() + self.route_rails() + self.place_input_inverters() + self.place_and_array() + self.route() + self.add_boundary() + self.DRC_LVS() + def setup_layout_constraints(self): + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.bus_layer = "m1" + self.bus_directions = None + self.bus_pitch = self.m1_pitch + self.bus_space = 1.5 * self.m1_space + self.input_layer = "li" + self.output_layer = "m2" + self.output_layer_pitch = self.m2_pitch + else: + self.bus_layer = "m2" + self.bus_directions = None + self.bus_pitch = self.m2_pitch + self.bus_space = self.m2_space + # This requires a special jog to ensure to conflicts with the output layers + self.input_layer = "m1" + self.output_layer = "m1" + self.output_layer_pitch = self.m1_pitch + self.height = self.number_of_outputs * self.and_mod.height # x offset for input inverters - self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space + # +1 input for spacing for supply rail contacts + self.x_off_inv_1 = (self.number_of_inputs + 1) * self.bus_pitch + self.bus_pitch - # x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches - self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch + # x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra bus pitches + self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.bus_pitch # x offset to output inverters self.width = self.x_off_and + self.and_mod.width @@ -79,28 +113,30 @@ class hierarchical_predecode(design.design): def route_rails(self): """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)] - offset = vector(0.5 * self.m2_width, self.m3_pitch) - self.input_rails = self.create_vertical_pin_bus(layer="m2", - offset=offset, - names=input_names, - length=self.height - 2 * self.m1_pitch) + # Offsets for the perimeter spacing to other modules + # This uses m3 pitch to leave space for power routes + offset = vector(self.bus_pitch, self.bus_pitch) + self.input_rails = self.create_vertical_bus(layer=self.bus_layer, + offset=offset, + names=input_names, + length=self.height - 2 * self.bus_pitch) invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)] non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] decode_names = invert_names + non_invert_names - offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch) - self.decode_rails = self.create_vertical_bus(layer="m2", + offset = vector(self.x_off_inv_1 + self.inv.width + self.bus_pitch, self.bus_pitch) + self.decode_rails = self.create_vertical_bus(layer=self.bus_layer, offset=offset, names=decode_names, - length=self.height - 2 * self.m1_pitch) + length=self.height - 2 * self.bus_pitch) def create_input_inverters(self): """ Create the input inverters to invert input signals for the decode stage. """ - self.in_inst = [] + self.inv_inst = [] for inv_num in range(self.number_of_inputs): name = "pre_inv_{0}".format(inv_num) - self.in_inst.append(self.add_inst(name=name, - mod=self.inv)) + self.inv_inst.append(self.add_inst(name=name, + mod=self.inv)) self.connect_inst(["in_{0}".format(inv_num), "inbar_{0}".format(inv_num), "vdd", "gnd"]) @@ -108,6 +144,7 @@ class hierarchical_predecode(design.design): def place_input_inverters(self): """ Place the input inverters to invert input signals for the decode stage. """ for inv_num in range(self.number_of_inputs): + if (inv_num % 2 == 0): y_off = inv_num * (self.inv.height) mirror = "R0" @@ -115,8 +152,8 @@ class hierarchical_predecode(design.design): y_off = (inv_num + 1) * (self.inv.height) mirror="MX" offset = vector(self.x_off_inv_1, y_off) - self.in_inst[inv_num].place(offset=offset, - mirror=mirror) + self.inv_inst[inv_num].place(offset=offset, + mirror=mirror) def create_and_array(self, connections): """ Create the AND stage for the decodes """ @@ -151,21 +188,30 @@ class hierarchical_predecode(design.design): def route_inputs_to_rails(self): """ Route the uninverted inputs to the second set of rails """ + + top_and_gate = self.and_inst[-1] for num in range(self.number_of_inputs): - # route one signal next to each vdd/gnd rail since this is - # typically where the p/n devices are and there are no - # pins in the and gates. - y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space + if num == 0: + pin = top_and_gate.get_pin("A") + elif num == 1: + pin = top_and_gate.get_pin("B") + elif num == 2: + pin = top_and_gate.get_pin("C") + elif num == 3: + 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) in_pos = vector(self.input_rails[in_pin].x, y_offset) a_pos = vector(self.decode_rails[a_pin].x, y_offset) - self.add_path("m1", [in_pos, a_pos]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", + self.add_path(self.input_layer, [in_pos, a_pos]) + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, offset=[self.input_rails[in_pin].x, y_offset]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, offset=[self.decode_rails[a_pin].x, y_offset]) def route_output_and(self): @@ -188,31 +234,47 @@ class hierarchical_predecode(design.design): for inv_num in range(self.number_of_inputs): out_pin = "Abar_{}".format(inv_num) in_pin = "in_{}".format(inv_num) + + inv_out_pin = self.inv_inst[inv_num].get_pin("Z") + inv_out_pos = inv_out_pin.rc() # add output so that it is just below the vdd or gnd rail # since this is where the p/n devices are and there are no # pins in the and gates. - y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space - inv_out_pin = self.in_inst[inv_num].get_pin("Z") - inv_out_pos = inv_out_pin.rc() - right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0) - rail_pos = vector(self.decode_rails[out_pin].x, y_offset) - self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + if OPTS.tech_name == "s8": + rail_pos = vector(self.decode_rails[out_pin].x, inv_out_pos.y) + self.add_path(self.output_layer, [inv_out_pos, rail_pos]) + else: + y_offset = (inv_num + 1) * self.inv.height - self.output_layer_pitch + right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").rx(), 0) + rail_pos = vector(self.decode_rails[out_pin].x, y_offset) + self.add_path(self.output_layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) + self.add_via_stack_center(from_layer=inv_out_pin.layer, - to_layer="m2", - offset=rail_pos) + to_layer=self.output_layer, + offset=inv_out_pos) + self.add_via_stack_center(from_layer=self.output_layer, + to_layer=self.bus_layer, + offset=rail_pos, + directions=self.bus_directions) # route input - pin = self.in_inst[inv_num].get_pin("A") - inv_in_pos = pin.lc() + pin = self.inv_inst[inv_num].get_pin("A") + inv_in_pos = pin.center() in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y) - self.add_path("m1", [in_pos, inv_in_pos]) + self.add_path(self.input_layer, [in_pos, inv_in_pos]) self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.input_layer, offset=inv_in_pos) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=in_pos) + via=self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=in_pos) + # Create the input pin at this location on the rail + self.add_layout_pin_rect_center(text=in_pin, + layer=self.bus_layer, + offset=in_pos, + height=via.mod.second_layer_height, + width=via.mod.second_layer_width) def route_and_to_rails(self): # This 2D array defines the connection mapping @@ -231,43 +293,67 @@ class hierarchical_predecode(design.design): pin = self.and_inst[k].get_pin(gate_pin) pin_pos = pin.center() rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) - self.add_path("m1", [rail_pos, pin_pos]) - self.add_via_stack_center(from_layer="m1", - to_layer="m2", - offset=rail_pos) + self.add_path(self.input_layer, [rail_pos, pin_pos]) + self.add_via_stack_center(from_layer=self.input_layer, + to_layer=self.bus_layer, + offset=rail_pos, + directions=self.bus_directions) if gate_pin == "A": direction = None else: direction = ("H", "H") self.add_via_stack_center(from_layer=pin.layer, - to_layer="m1", + to_layer=self.input_layer, offset=pin_pos, directions=direction) def route_vdd_gnd(self): """ Add a pin for each row of vdd/gnd which are must-connects next level up. """ - # Find the x offsets for where the vias/pins should be placed - in_xoffset = self.in_inst[0].rx() + self.m1_space - # out_xoffset = self.and_inst[0].cx() + self.m1_space - for num in range(0, self.number_of_outputs): - # this will result in duplicate polygons for rails, but who cares - - # Route both supplies + # In s8, we use hand-made decoder cells with vertical power + if OPTS.tech_name == "s8": for n in ["vdd", "gnd"]: - and_pin = self.and_inst[num].get_pin(n) - supply_offset = and_pin.ll().scale(0, 1) - self.add_rect(layer=and_pin.layer, - offset=supply_offset, - width=self.and_inst[num].rx()) + # This makes a wire from top to bottom for both inv and and gates + for i in [self.inv_inst, self.and_inst]: + bot_pins = i[0].get_pins(n) + top_pins = i[-1].get_pins(n) + for (bot_pin, top_pin) in zip(bot_pins, top_pins): + self.add_rect(layer=bot_pin.layer, + offset=vector(bot_pin.lx(), self.bus_pitch), + width=bot_pin.width(), + height=top_pin.uy() - self.bus_pitch) + # This adds power vias at the top of each cell + # (except the last to keep them inside the boundary) + for i in self.inv_inst[:-1:2] + self.and_inst[:-1:2]: + pins = i.get_pins(n) + for pin in pins: + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + self.add_power_pin(name=n, + loc=pin.uc(), + start_layer=pin.layer) + + # In other techs, we are using standard cell decoder cells with horizontal power + else: + for num in range(0, self.number_of_outputs): - # Add pins in two locations - for xoffset in [in_xoffset]: - pin_pos = vector(xoffset, and_pin.cy()) - self.add_power_pin(name=n, - loc=pin_pos, - start_layer=and_pin.layer) + # Route both supplies + for n in ["vdd", "gnd"]: + and_pins = self.and_inst[num].get_pins(n) + for and_pin in and_pins: + self.add_segment_center(layer=and_pin.layer, + start=vector(0, and_pin.cy()), + end=vector(self.width, and_pin.cy())) + + # Add pins in two locations + for xoffset in [self.inv_inst[0].lx() - self.bus_space, + self.and_inst[0].lx() - self.bus_space]: + pin_pos = vector(xoffset, and_pin.cy()) + self.add_power_pin(name=n, + loc=pin_pos, + start_layer=and_pin.layer) diff --git a/compiler/modules/hierarchical_predecode2x4.py b/compiler/modules/hierarchical_predecode2x4.py index b1aacc5a..9c7ddfa3 100644 --- a/compiler/modules/hierarchical_predecode2x4.py +++ b/compiler/modules/hierarchical_predecode2x4.py @@ -5,13 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc -import debug -import design -from vector import vector from hierarchical_predecode import hierarchical_predecode from globals import OPTS + class hierarchical_predecode2x4(hierarchical_predecode): """ Pre 2x4 decoder used in hierarchical_decoder. @@ -33,21 +30,6 @@ class hierarchical_predecode2x4(hierarchical_predecode): ["in_0", "in_1", "out_3", "vdd", "gnd"]] self.create_and_array(connections) - def create_layout(self): - """ The general organization is from left to right: - 1) a set of M2 rails for input signals - 2) a set of inverters to invert input signals - 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of AND gates for inversion - """ - self.setup_layout_constraints() - self.route_rails() - self.place_input_inverters() - self.place_and_array() - self.route() - self.add_boundary() - self.DRC_LVS() - def get_and_input_line_combination(self): """ These are the decoder connections of the AND gates to the A,B pins """ combination = [["Abar_0", "Abar_1"], diff --git a/compiler/modules/hierarchical_predecode3x8.py b/compiler/modules/hierarchical_predecode3x8.py index 4f2294f1..e8c44e48 100644 --- a/compiler/modules/hierarchical_predecode3x8.py +++ b/compiler/modules/hierarchical_predecode3x8.py @@ -5,13 +5,10 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from tech import drc -import debug -import design -from vector import vector from hierarchical_predecode import hierarchical_predecode from globals import OPTS + class hierarchical_predecode3x8(hierarchical_predecode): """ Pre 3x8 decoder used in hierarchical_decoder. @@ -37,22 +34,6 @@ class hierarchical_predecode3x8(hierarchical_predecode): ["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]] self.create_and_array(connections) - def create_layout(self): - """ - The general organization is from left to right: - 1) a set of M2 rails for input signals - 2) a set of inverters to invert input signals - 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs - 4) a set of NAND gates for inversion - """ - self.setup_layout_constraints() - self.route_rails() - self.place_input_inverters() - self.place_and_array() - self.route() - self.add_boundary() - self.DRC_LVS() - def get_and_input_line_combination(self): """ These are the decoder connections of the NAND gates to the A,B,C pins """ combination = [["Abar_0", "Abar_1", "Abar_2"], diff --git a/compiler/modules/hierarchical_predecode4x16.py b/compiler/modules/hierarchical_predecode4x16.py new file mode 100644 index 00000000..4a258bfb --- /dev/null +++ b/compiler/modules/hierarchical_predecode4x16.py @@ -0,0 +1,64 @@ +# 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 hierarchical_predecode import hierarchical_predecode +from globals import OPTS + + +class hierarchical_predecode4x16(hierarchical_predecode): + """ + Pre 4x16 decoder used in hierarchical_decoder. + """ + def __init__(self, name, height=None): + hierarchical_predecode.__init__(self, name, 4, height) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.add_modules() + self.create_input_inverters() + connections=[["inbar_0", "inbar_1", "inbar_2", "inbar_3", "out_0", "vdd", "gnd"], + ["in_0", "inbar_1", "inbar_2", "inbar_3", "out_1", "vdd", "gnd"], + ["inbar_0", "in_1", "inbar_2", "inbar_3", "out_2", "vdd", "gnd"], + ["in_0", "in_1", "inbar_2", "inbar_3", "out_3", "vdd", "gnd"], + ["inbar_0", "inbar_1", "in_2", "inbar_3", "out_4", "vdd", "gnd"], + ["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"] ] + + self.create_and_array(connections) + + def get_and_input_line_combination(self): + """ These are the decoder connections of the AND gates to the A,B pins """ + combination = [["Abar_0", "Abar_1", "Abar_2", "Abar_3"], + ["A_0", "Abar_1", "Abar_2", "Abar_3"], + ["Abar_0", "A_1", "Abar_2", "Abar_3"], + ["A_0", "A_1", "Abar_2", "Abar_3"], + ["Abar_0", "Abar_1", "A_2" , "Abar_3"], + ["A_0", "Abar_1", "A_2" , "Abar_3"], + ["Abar_0", "A_1", "A_2" , "Abar_3"], + ["A_0", "A_1", "A_2" , "Abar_3"], + ["Abar_0", "Abar_1", "Abar_2", "A_3"], + ["A_0", "Abar_1", "Abar_2", "A_3"], + ["Abar_0", "A_1", "Abar_2", "A_3"], + ["A_0", "A_1", "Abar_2", "A_3"], + ["Abar_0", "Abar_1", "A_2", "A_3"], + ["A_0", "Abar_1", "A_2", "A_3"], + ["Abar_0", "A_1", "A_2", "A_3"], + ["A_0", "A_1", "A_2", "A_3"]] + return combination diff --git a/compiler/modules/port_address.py b/compiler/modules/port_address.py index a27894ae..6293a79d 100644 --- a/compiler/modules/port_address.py +++ b/compiler/modules/port_address.py @@ -8,7 +8,7 @@ import debug import design from sram_factory import factory from vector import vector - +from tech import layer from globals import OPTS @@ -41,6 +41,10 @@ class port_address(design.design): self.create_wordline_driver() def create_layout(self): + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.place_instances() self.route_layout() self.DRC_LVS() @@ -85,11 +89,19 @@ class port_address(design.design): def route_internal(self): for row in range(self.num_rows): # The pre/post is to access the pin from "outside" the cell to avoid DRCs - decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() - driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() - mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0) - mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1) - self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) + decoder_out_pin = self.row_decoder_inst.get_pin("decode_{}".format(row)) + decoder_out_pos = decoder_out_pin.rc() + driver_in_pin = self.wordline_driver_inst.get_pin("in_{}".format(row)) + driver_in_pos = driver_in_pin.lc() + self.add_zjog(self.route_layer, decoder_out_pos, driver_in_pos) + + self.add_via_stack_center(from_layer=decoder_out_pin.layer, + to_layer=self.route_layer, + offset=decoder_out_pos) + + self.add_via_stack_center(from_layer=driver_in_pin.layer, + to_layer=self.route_layer, + offset=driver_in_pos) def add_modules(self): @@ -97,7 +109,7 @@ class port_address(design.design): num_outputs=self.num_rows) self.add_mod(self.row_decoder) - self.wordline_driver = factory.create(module_type="wordline_driver", + self.wordline_driver = factory.create(module_type="wordline_driver_array", rows=self.num_rows, cols=self.num_cols) self.add_mod(self.wordline_driver) @@ -139,7 +151,6 @@ class port_address(design.design): row_decoder_offset = vector(0, 0) wordline_driver_offset = vector(self.row_decoder.width, 0) - self.wordline_driver_inst.place(wordline_driver_offset) self.row_decoder_inst.place(row_decoder_offset) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 1f482f7d..fe8d3495 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -469,45 +469,37 @@ class port_data(design.design): bank_wmask_name = "bank_wmask_{}".format(bit) self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name) - def route_write_mask_and_array_to_write_driver(self,port): - """ Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write - mask AND array output and via for write driver enable """ + def route_write_mask_and_array_to_write_driver(self, port): + """ + Routing of wdriver_sel_{} between write mask AND array and + write driver array. Adds layout pin for write + mask AND array output and via for write driver enable + """ - inst1 = self.write_mask_and_array_inst - inst2 = self.write_driver_array_inst + wmask_inst = self.write_mask_and_array_inst + wdriver_inst = self.write_driver_array_inst - loc = 0 for bit in range(self.num_wmasks): # Bring write mask AND array output pin to port data level - self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) + self.copy_layout_pin(wmask_inst, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit)) - wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit)) - wdriver_en_pin = inst2.get_pin("en_{0}".format(bit)) + wmask_out_pin = wmask_inst.get_pin("wmask_out_{0}".format(bit)) + wdriver_en_pin = wdriver_inst.get_pin("en_{0}".format(bit)) - # The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the - # the wdriver_sel_{} pin in the write driver AND array. - if bit == 0: - # When the write mask output pin is right of the bitline, the target is found - while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()): - loc += 1 - length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch - debug.check(loc<=self.num_wmasks, - "Couldn't route the write mask select.") - else: - # Stride by the write size rather than finding the next pin to the right - loc += self.write_size - length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch + wmask_pos = wmask_out_pin.center() + wdriver_pos = wdriver_en_pin.rc() - vector(self.m2_pitch, 0) + mid_pos = vector(wdriver_pos.x, wmask_pos.y) - beg_pos = wmask_out_pin.center() - middle_pos = vector(length, wmask_out_pin.cy()) - end_pos = vector(length, wdriver_en_pin.cy()) + # Add driver on mask output + self.add_via_center(layers=self.m1_stack, + offset=wmask_pos) # Add via for the write driver array's enable input self.add_via_center(layers=self.m1_stack, - offset=end_pos) + offset=wdriver_pos) # Route between write mask AND array and write driver array - self.add_wire(self.m1_stack, [beg_pos, middle_pos, end_pos]) + self.add_wire(self.m1_stack, [wmask_pos, mid_pos, wdriver_pos]) def route_column_mux_to_precharge_array(self, port): """ Routing of BL and BR between col mux and precharge array """ @@ -516,15 +508,12 @@ class port_data(design.design): if self.col_addr_size==0: return - inst1 = self.column_mux_array_inst - inst2 = self.precharge_array_inst + start_bit = 1 if self.port == 0 else 0 - insn2_start_bit = 1 if self.port == 0 else 0 - - self.channel_route_bitlines(inst1=inst1, - inst2=inst2, - num_bits=self.num_cols, - inst2_start_bit=insn2_start_bit) + self.connect_bitlines(inst1=self.column_mux_array_inst, + inst2=self.precharge_array_inst, + num_bits=self.num_cols, + inst2_start_bit=start_bit) def route_sense_amp_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ diff --git a/compiler/modules/wordline_driver.py b/compiler/modules/wordline_driver_array.py similarity index 50% rename from compiler/modules/wordline_driver.py rename to compiler/modules/wordline_driver_array.py index 55f5e707..e3d7c4f9 100644 --- a/compiler/modules/wordline_driver.py +++ b/compiler/modules/wordline_driver_array.py @@ -7,15 +7,12 @@ # import debug import design -import math -from tech import drc +from tech import drc, layer from vector import vector from sram_factory import factory from globals import OPTS -from tech import cell_properties - -class wordline_driver(design.design): +class wordline_driver_array(design.design): """ Creates a Wordline Driver Generates the wordline-driver to drive the bitcell @@ -26,21 +23,9 @@ class wordline_driver(design.design): debug.info(1, "Creating {0}".format(self.name)) self.add_comment("rows: {0} cols: {1}".format(rows, cols)) - self.bitcell_rows = rows - self.bitcell_cols = cols + self.rows = rows + self.cols = cols - b = factory.create(module_type="bitcell") - try: - self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple - except AttributeError: - self.cell_multiple = 1 - self.cell_height = self.cell_multiple * b.height - - # We may have more than one bitcell per decoder row - self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows) - self.create_netlist() if not OPTS.netlist_only: self.create_layout() @@ -51,7 +36,10 @@ class wordline_driver(design.design): self.create_drivers() def create_layout(self): - self.setup_layout_constants() + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" self.place_drivers() self.route_layout() self.route_vdd_gnd() @@ -61,104 +49,99 @@ class wordline_driver(design.design): def add_pins(self): # inputs to wordline_driver. - for i in range(self.bitcell_rows): + for i in range(self.rows): self.add_pin("in_{0}".format(i), "INPUT") # Outputs from wordline_driver. - for i in range(self.bitcell_rows): + for i in range(self.rows): self.add_pin("wl_{0}".format(i), "OUTPUT") self.add_pin("en", "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") def add_modules(self): - self.and2 = factory.create(module_type="pand2", - height=self.cell_height, - size=self.bitcell_cols) - self.add_mod(self.and2) + self.wl_driver = factory.create(module_type="wordline_driver", + 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. """ - - # Find the x offsets for where the vias/pins should be placed - xoffset_list = [self.and_inst[0].lx()] - for num in range(self.bitcell_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.and2.height, - num) - # Route both supplies + if OPTS.tech_name == "s8": for name in ["vdd", "gnd"]: - supply_pin = self.and_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) + 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.and_inst = [] - for row in range(self.bitcell_rows): + self.wld_inst = [] + for row in range(self.rows): name_and = "wl_driver_and{}".format(row) # add and2 - self.and_inst.append(self.add_inst(name=name_and, - mod=self.and2)) + self.wld_inst.append(self.add_inst(name=name_and, + mod=self.wl_driver)) self.connect_inst(["in_{0}".format(row), "en", "wl_{0}".format(row), "vdd", "gnd"]) - def setup_layout_constants(self): - # We may have more than one bitcell per decoder row - self.driver_rows = math.ceil(self.bitcell_rows / self.cell_multiple) - # We will place this many final decoders per row - self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows) - 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" + + and2_offset = [self.wl_driver.width, y_offset] + + # add and2 + self.wld_inst[row].place(offset=and2_offset, + mirror=inst_mirror) + # Leave a well gap to separate the bitcell array well from this well well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active") - - self.width = self.decoders_per_row * self.and2.width + well_gap - self.height = self.and2.height * self.driver_rows - - for inst_index in range(self.bitcell_rows): - row = math.floor(inst_index / self.decoders_per_row) - dec = inst_index % self.decoders_per_row - - if (row % 2): - y_offset = self.and2.height * (row + 1) - inst_mirror = "MX" - else: - y_offset = self.and2.height * row - inst_mirror = "R0" - - x_offset = dec * self.and2.width - and2_offset = [x_offset, y_offset] - - # add and2 - self.and_inst[inst_index].place(offset=and2_offset, - mirror=inst_mirror) + self.width = self.wl_driver.width + well_gap + self.height = self.wl_driver.height * self.rows def route_layout(self): """ Route all of the signals """ # Wordline enable connection - en_pin = self.and_inst[0].get_pin("B") + en_pin = self.wld_inst[0].get_pin("B") en_bottom_pos = vector(en_pin.lx(), 0) en_pin = self.add_layout_pin(text="en", layer="m2", offset=en_bottom_pos, height=self.height) - for inst_index in range(self.bitcell_rows): - and_inst = self.and_inst[inst_index] - row = math.floor(inst_index / self.decoders_per_row) + for row in range(self.rows): + and_inst = self.wld_inst[row] # Drop a via b_pin = and_inst.get_pin("B") @@ -167,18 +150,12 @@ class wordline_driver(design.design): offset=b_pin.center()) # connect the decoder input pin to and2 A - a_pin = and_inst.get_pin("A") - a_pos = a_pin.center() - # must under the clk line in M1 - self.add_layout_pin_segment_center(text="in_{0}".format(row), - layer="m1", - start=vector(0, a_pos.y), - end=a_pos) + self.copy_layout_pin(and_inst, "A", "in_{0}".format(row)) # output each WL on the right wl_offset = and_inst.get_pin("Z").rc() self.add_layout_pin_segment_center(text="wl_{0}".format(row), - layer="m1", + layer=self.route_layer, start=wl_offset, end=wl_offset - vector(self.m1_width, 0)) @@ -189,7 +166,7 @@ class wordline_driver(design.design): """ stage_effort_list = [] - stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise) + stage1 = self.wl_driver.get_stage_effort(external_cout, inp_is_rise) stage_effort_list.append(stage1) return stage_effort_list @@ -200,5 +177,5 @@ class wordline_driver(design.design): the enable connections in the bank """ # The enable is connected to a and2 for every row. - total_cin = self.and2.get_cin() * self.rows + total_cin = self.wl_driver.get_cin() * self.rows return total_cin diff --git a/compiler/options.py b/compiler/options.py index 0e41f74f..646b2a68 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -135,6 +135,10 @@ class options(optparse.Values): dff_array = "dff_array" dff = "dff" dummy_bitcell = "dummy_bitcell" + inv_dec = "pinv" + nand2_dec = "pnand2" + nand3_dec = "pnand3" + nand4_dec = "pnand4" # Not available right now precharge_array = "precharge_array" ptx = "ptx" replica_bitcell = "replica_bitcell" diff --git a/compiler/pgates/pand2.py b/compiler/pgates/pand2.py index 41189876..435ace1f 100644 --- a/compiler/pgates/pand2.py +++ b/compiler/pgates/pand2.py @@ -13,7 +13,7 @@ from sram_factory import factory class pand2(pgate.pgate): """ - This is a simple buffer used for driving loads. + This is an AND (or NAND) with configurable drive strength. """ def __init__(self, name, size=1, height=None, vertical=False, add_wells=True): debug.info(1, "Creating pand2 {}".format(name)) diff --git a/compiler/pgates/pdriver.py b/compiler/pgates/pdriver.py index 266400c9..578a11c4 100644 --- a/compiler/pgates/pdriver.py +++ b/compiler/pgates/pdriver.py @@ -17,13 +17,13 @@ class pdriver(pgate.pgate): sized for driving a load. """ - def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None, add_wells=True): + def __init__(self, name, inverting=False, fanout=0, size_list=None, height=None, add_wells=True): debug.info(1, "creating pdriver {}".format(name)) self.stage_effort = 3 self.height = height - self.neg_polarity = neg_polarity + self.inverting = inverting self.size_list = size_list self.fanout = fanout @@ -31,8 +31,8 @@ class pdriver(pgate.pgate): debug.error("Either fanout or size list must be specified.", -1) if self.size_list and self.fanout != 0: debug.error("Cannot specify both size_list and fanout.", -1) - if self.size_list and self.neg_polarity: - debug.error("Cannot specify both size_list and neg_polarity.", -1) + if self.size_list and self.inverting: + debug.error("Cannot specify both size_list and inverting.", -1) # Creates the netlist and layout pgate.pgate.__init__(self, name, height, add_wells) @@ -47,9 +47,9 @@ class pdriver(pgate.pgate): int(round(self.fanout ** (1 / self.stage_effort)))) # Increase the number of stages if we need to fix polarity - if self.neg_polarity and (self.num_stages % 2 == 0): + if self.inverting and (self.num_stages % 2 == 0): self.num_stages += 1 - elif not self.neg_polarity and (self.num_stages % 2): + elif not self.inverting and (self.num_stages % 2): self.num_stages += 1 self.size_list = [] diff --git a/compiler/pgates/pgate.py b/compiler/pgates/pgate.py index cdb89fcb..a8c45641 100644 --- a/compiler/pgates/pgate.py +++ b/compiler/pgates/pgate.py @@ -44,7 +44,6 @@ class pgate(design.design): self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer)) # This is the space from a S/D contact to the supply rail - # Assume the contact starts at the active edge contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space # This is a poly-to-poly of a flipped cell poly_to_poly_gate_space = self.poly_extend_active + self.poly_space diff --git a/compiler/pgates/pinv_dec.py b/compiler/pgates/pinv_dec.py new file mode 100644 index 00000000..efc21074 --- /dev/null +++ b/compiler/pgates/pinv_dec.py @@ -0,0 +1,217 @@ +# 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 contact +import pinv +import debug +from tech import drc, parameter +from vector import vector +from globals import OPTS +from sram_factory import factory + +if(OPTS.tech_name == "s8"): + from tech import nmos_bins, pmos_bins, accuracy_requirement + + +class pinv_dec(pinv.pinv): + """ + This is another version of pinv but with layout for the decoder. + Other stuff is the same (netlist, sizes, etc.) + """ + + def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): + + debug.info(2, + "creating pinv_dec structure {0} with size of {1}".format(name, + size)) + if not height: + b = factory.create(module_type="bitcell") + self.cell_height = b.height + else: + self.cell_height = height + + # Inputs to cells are on input layer + # Outputs from cells are on output layer + if OPTS.tech_name == "s8": + self.supply_layer = "m1" + else: + self.supply_layer = "m2" + + pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells) + + def determine_tx_mults(self): + """ + Determines the number of fingers needed to achieve the size within + the height constraint. This may fail if the user has a tight height. + """ + + # This is always 1 tx, because we have horizontal transistors. + self.tx_mults = 1 + self.nmos_width = self.nmos_size * drc("minwidth_tx") + self.pmos_width = self.pmos_size * drc("minwidth_tx") + if OPTS.tech_name == "s8": + (self.nmos_width, self.tx_mults) = self.bin_width("nmos", self.nmos_width) + (self.pmos_width, self.tx_mults) = self.bin_width("pmos", self.pmos_width) + return + + # Over-ride the route input gate to call the horizontal version. + # Other top-level netlist and layout functions are not changed. + def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None): + """ + Route the input gate to the left side of the cell for access. + Position is actually ignored and is left to be compatible with the pinv. + """ + + nmos_gate_pin = nmos_inst.get_pin("G") + pmos_gate_pin = pmos_inst.get_pin("G") + + # Check if the gates are aligned and give an error if they aren't! + if nmos_gate_pin.ll().y != pmos_gate_pin.ll().y: + self.gds_write("unaliged_gates.gds") + debug.check(nmos_gate_pin.ll().y == pmos_gate_pin.ll().y, + "Connecting unaligned gates not supported. See unaligned_gates.gds.") + + # Pick point on the left of NMOS and up to PMOS + nmos_gate_pos = nmos_gate_pin.rc() + pmos_gate_pos = pmos_gate_pin.lc() + self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) + + # Center is completely symmetric. + contact_width = contact.poly_contact.width + contact_offset = nmos_gate_pin.lc() \ + - vector(self.poly_extend_active + 0.5 * contact_width, 0) + via = self.add_via_stack_center(from_layer="poly", + to_layer=self.route_layer, + offset=contact_offset, + directions=directions) + self.add_path("poly", [contact_offset, nmos_gate_pin.lc()]) + + self.add_layout_pin_rect_center(text=name, + layer=self.route_layer, + offset=contact_offset, + width=via.mod.second_layer_width, + height=via.mod.second_layer_height) + + def determine_width(self): + self.width = self.pmos_inst.rx() + self.well_extend_active + + def extend_wells(self): + """ Extend bottom to top for each well. """ + + from tech import layer + if "pwell" in layer: + ll = self.nmos_inst.ll() - self.nmos_inst.mod.active_offset + ur = self.nmos_inst.ur() + self.nmos_inst.mod.active_offset + self.add_rect(layer="pwell", + offset=ll, + width=ur.x - ll.x, + height=self.height - ll.y) + + if "nwell" in layer: + ll = self.pmos_inst.ll() - self.pmos_inst.mod.active_offset + ur = self.pmos_inst.ur() + self.pmos_inst.mod.active_offset + self.add_rect(layer="nwell", + offset=ll - vector(self.nwell_enclose_active, 0), + width=ur.x - ll.x + self.nwell_enclose_active, + height=self.height - ll.y + 2 * self.nwell_enclose_active) + + def place_ptx(self): + """ + """ + + # offset so that the input contact is over from the left edge by poly spacing + x_offset = self.nmos.active_offset.y + contact.poly_contact.width + self.poly_space + # center the transistor in the y-dimension + y_offset = self.nmos.width + self.active_space + self.nmos_pos = vector(x_offset, y_offset) + self.nmos_inst.place(self.nmos_pos) + self.nmos_inst.place(self.nmos_pos, + rotate=270) + # place PMOS so it is half a poly spacing down from the top + xoffset = self.nmos_inst.height + 2 * self.poly_extend_active + 2 * self.well_extend_active + drc("pwell_to_nwell") + self.pmos_pos = self.nmos_pos + vector(xoffset, 0) + self.pmos_inst.place(self.pmos_pos, + rotate=270) + + # Output position will be in between the PMOS and NMOS drains + pmos_drain_pos = self.pmos_inst.get_pin("D").center() + nmos_drain_pos = self.nmos_inst.get_pin("D").center() + self.output_pos = vector(0.5 * (pmos_drain_pos.x + nmos_drain_pos.x), nmos_drain_pos.y) + + def route_outputs(self): + """ + Route the output (drains) together. + Optionally, routes output to edge. + """ + + # Get the drain pin + nmos_drain_pin = self.nmos_inst.get_pin("D") + + # Pick point at right most of NMOS and connect over to PMOS + nmos_drain_pos = nmos_drain_pin.lc() + right_side = vector(self.width, nmos_drain_pos.y) + + self.add_layout_pin_segment_center("Z", + self.route_layer, + nmos_drain_pos, + right_side) + + def add_well_contacts(self): + """ Add n/p well taps to the layout and connect to supplies """ + + source_pos = self.pmos_inst.get_pin("S").center() + contact_pos = vector(source_pos.x, self.height) + self.nwell_contact = self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="n", + well_type="n") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + source_pos = self.nmos_inst.get_pin("S").center() + contact_pos = vector(source_pos.x, self.height) + self.pwell_contact= self.add_via_center(layers=self.active_stack, + offset=contact_pos, + implant_type="p", + well_type="p") + self.add_via_stack_center(offset=contact_pos, + from_layer=self.active_stack[2], + to_layer=self.supply_layer) + + def route_supply_rails(self): + pin = self.nmos_inst.get_pin("S") + source_pos = pin.center() + bottom_pos = source_pos.scale(1, 0) + top_pos = bottom_pos + vector(0, self.height) + self.add_layout_pin_segment_center("gnd", + self.supply_layer, + start=bottom_pos, + end=top_pos) + + pin = self.pmos_inst.get_pin("S") + source_pos = pin.center() + bottom_pos = source_pos.scale(1, 0) + top_pos = bottom_pos + vector(0, self.height) + self.add_layout_pin_segment_center("vdd", + self.supply_layer, + start=bottom_pos, + end=top_pos) + + def connect_rails(self): + """ Connect the nmos and pmos to its respective power rails """ + + source_pos = self.nmos_inst.get_pin("S").center() + self.add_via_stack_center(offset=source_pos, + from_layer=self.route_layer, + to_layer=self.supply_layer) + + source_pos = self.pmos_inst.get_pin("S").center() + self.add_via_stack_center(offset=source_pos, + from_layer=self.route_layer, + to_layer=self.supply_layer) + diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 85b3f0b6..6cbd7cca 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -212,7 +212,10 @@ class pnand3(pgate.pgate): 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 + # This is a more compact offset, but the bottom one works better in the decoders to "center" the pins + # in the height of the gates self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space + # self.inputA_yoffset = self.output_yoffset - self.m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index c584e70b..f6716a1f 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -386,10 +386,12 @@ class ptx(design.design): well_ll = center_pos - vector(0.5 * self.well_width, 0.5 * self.well_height) if well_name in layer: - self.add_rect(layer=well_name, - offset=well_ll, - width=self.well_width, - height=self.well_height) + well = self.add_rect(layer=well_name, + offset=well_ll, + width=self.well_width, + height=self.well_height) + setattr(self, well_name, well) + if "vtg" in layer: self.add_rect(layer="vtg", offset=well_ll, diff --git a/compiler/pgates/wordline_driver.py b/compiler/pgates/wordline_driver.py new file mode 100644 index 00000000..1b035e20 --- /dev/null +++ b/compiler/pgates/wordline_driver.py @@ -0,0 +1,155 @@ +# 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 design +from sram_factory import factory +from globals import OPTS +from tech import layer + + +class wordline_driver(design.design): + """ + This is an AND (or NAND) with configurable drive strength to drive the wordlines. + It is matched to the bitcell height. + """ + def __init__(self, name, size=1, height=None): + debug.info(1, "Creating wordline_driver {}".format(name)) + self.add_comment("size: {}".format(size)) + design.design.__init__(self, name) + + if height is None: + b = factory.create(module_type="bitcell") + self.height = b.height + else: + self.height = height + self.size = size + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def create_netlist(self): + self.add_pins() + self.create_modules() + self.create_insts() + + def create_modules(self): + if OPTS.tech_name == "s8": + self.nand = factory.create(module_type="nand2_dec") + self.height = self.nand.height + else: + self.nand = factory.create(module_type="nand2_dec", + height=self.height) + + self.driver = factory.create(module_type="inv_dec", + size=self.size, + height=self.nand.height) + + self.add_mod(self.nand) + self.add_mod(self.driver) + + def create_layout(self): + self.width = self.nand.width + self.driver.width + if "li" in layer: + self.route_layer = "li" + else: + self.route_layer = "m1" + + self.place_insts() + self.route_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("Z", "OUTPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def create_insts(self): + self.nand_inst = self.add_inst(name="wld_nand", + mod=self.nand) + self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"]) + + self.driver_inst = self.add_inst(name="wl_driver", + mod=self.driver) + 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)) + + # Add INV to the right + self.driver_inst.place(offset=vector(self.nand_inst.rx(), 0)) + + def route_supply_rails(self): + """ Add vdd/gnd rails to the top, (middle), and bottom. """ + if OPTS.tech_name == "s8": + for name in ["vdd", "gnd"]: + for inst in [self.nand_inst, self.driver_inst]: + self.copy_layout_pin(inst, name) + else: + self.add_layout_pin_rect_center(text="gnd", + layer=self.route_layer, + offset=vector(0.5 * self.width, 0), + width=self.width) + + 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 route_wires(self): + + # nand Z to inv A + z1_pin = self.nand_inst.get_pin("Z") + a2_pin = self.driver_inst.get_pin("A") + if OPTS.tech_name == "s8": + mid1_point = vector(a2_pin.cx(), z1_pin.cy()) + else: + mid1_point = vector(z1_pin.cx(), a2_pin.cy()) + self.add_path(self.route_layer, + [z1_pin.center(), mid1_point, a2_pin.center()]) + + def add_layout_pins(self): + pin = self.driver_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"]: + 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 get_stage_efforts(self, external_cout, inp_is_rise=False): + """Get the stage efforts of the A or B -> Z path""" + stage_effort_list = [] + stage1_cout = self.driver.get_cin() + stage1 = self.nand.get_stage_effort(stage1_cout, inp_is_rise) + stage_effort_list.append(stage1) + + stage2 = self.driver.get_stage_effort(external_cout, stage1.is_rise) + stage_effort_list.append(stage2) + + return stage_effort_list + + def get_cin(self): + """Return the relative input capacitance of a single input""" + return self.nand.get_cin() + diff --git a/compiler/sram_factory.py b/compiler/sram_factory.py index 110dbe15..df6b9d99 100644 --- a/compiler/sram_factory.py +++ b/compiler/sram_factory.py @@ -77,7 +77,9 @@ class sram_factory: """ tech_module_type, tm_overridden = self.get_techmodule_type(module_type) user_module_type, um_overridden = self.get_usermodule_type(module_type) - + #print(module_type, tech_module_type, tm_overridden) + #print(module_type, user_module_type, um_overridden) + # overridden user modules have priority if um_overridden: real_module_type = user_module_type diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 0acc8926..ed15770d 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -35,7 +35,7 @@ class library_lvs_test(openram_test): debug.error("Missing GDS file {}".format(gds_name)) if not os.path.isfile(sp_name): lvs_errors += 1 - debug.error("Missing SPICE file {}".format(gds_name)) + debug.error("Missing SPICE file {}".format(sp_name)) drc_errors += verify.run_drc(name, gds_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name) diff --git a/compiler/tests/04_pand2_dec_test.py b/compiler/tests/04_pand2_dec_test.py new file mode 100755 index 00000000..92b80203 --- /dev/null +++ b/compiler/tests/04_pand2_dec_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 +from sram_factory import factory +import debug + +class pand2_dec_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 pand2_dec + + debug.info(2, "Testing pand2 gate 4x") + a = pand2_dec.pand2_dec(name="pand2x4", 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_pand3_dec_test.py b/compiler/tests/04_pand3_dec_test.py new file mode 100755 index 00000000..78e576ed --- /dev/null +++ b/compiler/tests/04_pand3_dec_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 +from sram_factory import factory +import debug + +class pand3_dec_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 pand3_dec + + debug.info(2, "Testing pand3 gate 4x") + a = pand3_dec.pand3_dec(name="pand3x4", 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_pand4_dec_test.py b/compiler/tests/04_pand4_dec_test.py new file mode 100755 index 00000000..78e576ed --- /dev/null +++ b/compiler/tests/04_pand4_dec_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 +from sram_factory import factory +import debug + +class pand3_dec_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 pand3_dec + + debug.info(2, "Testing pand3 gate 4x") + a = pand3_dec.pand3_dec(name="pand3x4", 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_pdriver_test.py b/compiler/tests/04_pdriver_test.py index e65b6fad..41af86f6 100755 --- a/compiler/tests/04_pdriver_test.py +++ b/compiler/tests/04_pdriver_test.py @@ -32,13 +32,13 @@ class pdriver_test(openram_test): c = factory.create(module_type="pdriver", fanout = 50) self.local_check(c) - d = factory.create(module_type="pdriver", fanout = 50, neg_polarity = True) + d = factory.create(module_type="pdriver", fanout = 50, inverting = True) self.local_check(d) e = factory.create(module_type="pdriver", fanout = 64) self.local_check(e) - f = factory.create(module_type="pdriver", fanout = 64, neg_polarity = True) + f = factory.create(module_type="pdriver", fanout = 64, inverting = True) self.local_check(f) globals.end_openram() diff --git a/compiler/tests/04_pinv_dec_1x_test.py b/compiler/tests/04_pinv_dec_1x_test.py new file mode 100755 index 00000000..8876ab58 --- /dev/null +++ b/compiler/tests/04_pinv_dec_1x_test.py @@ -0,0 +1,35 @@ +#!/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 pinv_dec_1x_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 1x size decoder inverter") + tx = factory.create(module_type="pinv_dec", size=1) + 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()) diff --git a/compiler/tests/08_wordline_driver_test.py b/compiler/tests/04_wordline_driver_test.py similarity index 93% rename from compiler/tests/08_wordline_driver_test.py rename to compiler/tests/04_wordline_driver_test.py index 8a18a59d..ada65db7 100755 --- a/compiler/tests/08_wordline_driver_test.py +++ b/compiler/tests/04_wordline_driver_test.py @@ -25,7 +25,7 @@ class wordline_driver_test(openram_test): # check wordline driver for single port debug.info(2, "Checking driver") - tx = factory.create(module_type="wordline_driver", rows=8, cols=32) + tx = factory.create(module_type="wordline_driver") self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 07d995f9..7d155b01 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -21,11 +21,17 @@ class hierarchical_decoder_test(openram_test): 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) self.local_check(a) - + # Checks 2x4 and 2-input NAND decoder with non-power-of-two debug.info(1, "Testing 17 row sample for hierarchical_decoder") a = factory.create(module_type="hierarchical_decoder", num_outputs=17) diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py index ebb06330..94fd4838 100755 --- a/compiler/tests/06_hierarchical_predecode2x4_test.py +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -21,7 +21,12 @@ class hierarchical_predecode2x4_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # checking hierarchical precode 2x4 for single port + # 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 + debug.info(1, "Testing sample for hierarchy_predecode2x4") a = factory.create(module_type="hierarchical_predecode2x4") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py index 63acc416..33f94d9d 100755 --- a/compiler/tests/06_hierarchical_predecode3x8_test.py +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -21,7 +21,12 @@ class hierarchical_predecode3x8_test(openram_test): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - # checking hierarchical precode 3x8 for single port + # 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 + debug.info(1, "Testing sample for hierarchy_predecode3x8") a = factory.create(module_type="hierarchical_predecode3x8") self.local_check(a) diff --git a/compiler/tests/06_hierarchical_predecode4x16_test.py b/compiler/tests/06_hierarchical_predecode4x16_test.py new file mode 100755 index 00000000..b4ebda38 --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode4x16_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 + +@unittest.skip("SKIPPING hierarchical_predecode4x16_test") +class hierarchical_predecode4x16_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 + + debug.info(1, "Testing sample for hierarchy_predecode4x16") + a = factory.create(module_type="hierarchical_predecode4x16") + self.local_check(a) + + 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()) diff --git a/compiler/tests/08_wordline_driver_pbitcell_test.py b/compiler/tests/08_wordline_driver_array_pbitcell_test.py similarity index 87% rename from compiler/tests/08_wordline_driver_pbitcell_test.py rename to compiler/tests/08_wordline_driver_array_pbitcell_test.py index 3dd5933d..267aaddf 100755 --- a/compiler/tests/08_wordline_driver_pbitcell_test.py +++ b/compiler/tests/08_wordline_driver_array_pbitcell_test.py @@ -15,9 +15,8 @@ from globals import OPTS from sram_factory import factory import debug -#@unittest.skip("SKIPPING 04_driver_test") -class wordline_driver_pbitcell_test(openram_test): +class wordline_driver_array_pbitcell_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) @@ -31,7 +30,7 @@ class wordline_driver_pbitcell_test(openram_test): factory.reset() debug.info(2, "Checking driver (multi-port case)") - tx = factory.create(module_type="wordline_driver", rows=8, cols=64) + tx = factory.create(module_type="wordline_driver_array", rows=8, cols=64) self.local_check(tx) globals.end_openram() diff --git a/compiler/tests/08_wordline_driver_array_test.py b/compiler/tests/08_wordline_driver_array_test.py new file mode 100755 index 00000000..3491cc4f --- /dev/null +++ b/compiler/tests/08_wordline_driver_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_driver_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_driver_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()) diff --git a/compiler/tests/18_port_data_1rw_1r_test.py b/compiler/tests/18_port_data_1rw_1r_test.py index 6201de6a..e9e282a5 100755 --- a/compiler/tests/18_port_data_1rw_1r_test.py +++ b/compiler/tests/18_port_data_1rw_1r_test.py @@ -20,10 +20,10 @@ class port_data_1rw_1r_test(openram_test): globals.init_openram(config_file) from sram_config import sram_config - OPTS.bitcell = "bitcell_1w_1r" - OPTS.num_rw_ports = 0 + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 + OPTS.num_w_ports = 0 c = sram_config(word_size=4, num_words=16) diff --git a/compiler/tests/19_single_bank_test.py b/compiler/tests/19_single_bank_test.py index 38a847a9..6cff481b 100755 --- a/compiler/tests/19_single_bank_test.py +++ b/compiler/tests/19_single_bank_test.py @@ -25,28 +25,28 @@ class single_bank_test(openram_test): c = sram_config(word_size=4, num_words=16) - # c.words_per_row=1 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "No column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.words_per_row=1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) - # c.num_words=32 - # c.words_per_row=2 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "Two way column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.num_words=32 + c.words_per_row=2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) - # c.num_words=64 - # c.words_per_row=4 - # factory.reset() - # c.recompute_sizes() - # debug.info(1, "Four way column mux") - # a = factory.create("bank", sram_config=c) - # self.local_check(a) + c.num_words=64 + c.words_per_row=4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("bank", sram_config=c) + self.local_check(a) c.word_size=2 c.num_words=128 diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 99d534d0..4cae1a9c 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -60,6 +60,7 @@ class openram_test(unittest.TestCase): #debug.info(0,"Archiving failed files to {}.zip".format(zip_file)) #shutil.make_archive(zip_file, 'zip', OPTS.openram_temp) debug.warning("DRC failed but LVS passed: {}".format(a.name)) + #self.fail("DRC failed but LVS passed: {}".format(a.name)) elif drc_result != 0: #zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid()) #debug.info(0,"Archiving failed files to {}.zip".format(zip_file))