diff --git a/.gitignore b/.gitignore index a045c64a..cb5e29e1 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ sky130A/ sky130B/ skywater-pdk/ sky130_fd_bd_sram/ +docker/openram-ubuntu.log diff --git a/Makefile b/Makefile index e10caba0..18768dd4 100644 --- a/Makefile +++ b/Makefile @@ -97,6 +97,7 @@ $(SRAM_LIB_DIR): check-pdk-root @echo "Cloning SRAM library..." @[ -d $(SRAM_LIB_DIR) ] || \ git clone $(SRAM_LIB_GIT_REPO) $(SRAM_LIB_DIR) + @git -C $(SRAM_LIB_DIR) fetch @git -C $(SRAM_LIB_DIR) checkout $(SRAM_LIB_GIT_COMMIT) install: $(SRAM_LIB_DIR) diff --git a/VERSION b/VERSION index 53fc08d6..f6b839d3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.20 +1.2.28 diff --git a/compiler/base/contact.py b/compiler/base/contact.py index 5fe59bcb..84f60d0e 100644 --- a/compiler/base/contact.py +++ b/compiler/base/contact.py @@ -66,8 +66,6 @@ class contact(hierarchy_design): self.offset = vector(0, 0) self.implant_type = implant_type self.well_type = well_type - # Module does not have pins, but has empty pin list. - self.pins = [] self.create_layout() def create_layout(self): diff --git a/compiler/base/design.py b/compiler/base/design.py index 94fd407f..69d213d0 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -41,17 +41,17 @@ class design(hierarchy_design): if prop and prop.hard_cell: # The pins get added from the spice file, so just check # that they matched here - debug.check(prop.port_names == self.pins, - "Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins)) + debug.check(prop.port_names == list(self.pins), + "Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, list(self.pins))) self.add_pin_indices(prop.port_indices) self.add_pin_names(prop.port_map) - self.add_pin_types(prop.port_types) + self.update_pin_types(prop.port_types) (width, height) = utils.get_libcell_size(self.cell_name, GDS["unit"], layer[prop.boundary_layer]) - self.pin_map = utils.get_libcell_pins(self.pins, + self.pin_map = utils.get_libcell_pins(list(self.pins), self.cell_name, GDS["unit"]) @@ -126,5 +126,3 @@ class design(hierarchy_design): for inst in self.insts: total_module_power += inst.mod.analytical_power(corner, load) return total_module_power - - diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 9e7172bf..0f26f02e 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -161,8 +161,8 @@ class geometry: class instance(geometry): """ - An instance of an instance/module with a specified location and - rotation + An instance of a module with a specified location, rotation, + spice pins, and spice nets """ def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False): """Initializes an instance to represent a module""" @@ -177,6 +177,18 @@ class instance(geometry): self.offset = vector(offset).snap_to_grid() self.mirror = mirror self.is_bitcell = is_bitcell + # track if the instance's spice pin connections have been made + self.connected = False + + # deepcopy because this instance needs to + # change attributes in these spice objects + self.spice_pins = copy.deepcopy(self.mod.pins) + self.spice_nets = copy.deepcopy(self.mod.nets) + for pin in self.spice_pins.values(): + pin.set_inst(self) + for net in self.spice_nets.values(): + net.set_inst(self) + if OPTS.netlist_only: self.width = 0 self.height = 0 @@ -275,6 +287,34 @@ class instance(geometry): new_pins.append(p) return new_pins + def connect_spice_pins(self, nets_list): + """ + add the connection between instance pins and module nets + to both of their respective objects + nets_list must be the same length as self.spice_pins + """ + if len(nets_list) == 0 and len(self.spice_pins) == 0: + # this is the only valid case to skip the following debug check + # because this with no pins are often connected arbitrarily + self.connected = True + return + debug.check(not self.connected, + "instance {} has already been connected".format(self.name)) + debug.check(len(self.spice_pins) == len(nets_list), + "must provide list of nets the same length as pin list\ + when connecting an instance") + for pin in self.spice_pins.values(): + net = nets_list.pop(0) + pin.set_inst_net(net) + net.connect_pin(pin) + self.connected = True + + def get_connections(self): + conns = [] + for pin in self.spice_pins.values(): + conns.append(pin.inst_net.name) + return conns + def calculate_transform(self, node): #set up the rotation matrix angle = math.radians(float(node.rotate)) diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index 42c0543d..31a1e191 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -135,11 +135,11 @@ class hierarchy_design(spice, layout): # Translate port names to external nets if len(port_nets) != len(self.pins): debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, - self.pins), + list(self.pins)), 1) - port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} debug.info(3, "Instance name={}".format(inst_name)) - for subinst, conns in zip(self.insts, self.conns): + for subinst, conns in zip(self.insts, self.get_instance_connections()): if subinst in self.graph_inst_exclude: continue subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name @@ -153,11 +153,11 @@ class hierarchy_design(spice, layout): # Translate port names to external nets if len(port_nets) != len(self.pins): debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, - self.pins), + list(self.pins)), 1) - port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + port_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} debug.info(3, "Instance name={}".format(inst_name)) - for subinst, conns in zip(self.insts, self.conns): + for subinst, conns in zip(self.insts, self.get_instance_connections()): subinst_name = inst_name + "{}x".format(OPTS.hier_seperator) + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) for si_port, conn in zip(subinst_ports, conns): @@ -186,7 +186,7 @@ class hierarchy_design(spice, layout): """ # The final pin names will depend on the spice hierarchy, so # they are passed as an input. - pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} input_pins = self.get_inputs() output_pins = self.get_outputs() inout_pins = self.get_inouts() @@ -197,7 +197,7 @@ class hierarchy_design(spice, layout): def __str__(self): """ override print function output """ - pins = ",".join(self.pins) + pins = ",".join(list(self.pins)) insts = [" {}".format(x) for x in self.insts] objs = [" {}".format(x) for x in self.objs] s = "********** design {0} **********".format(self.cell_name) @@ -208,7 +208,7 @@ class hierarchy_design(spice, layout): def __repr__(self): """ override print function output """ - text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n" + text="( design: " + self.name + " pins=" + str(list(self.pins)) + " " + str(self.width) + "x" + str(self.height) + " )\n" for i in self.objs: text+=str(i) + ",\n" for i in self.insts: diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 8c595254..7965f87c 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -641,7 +641,7 @@ class layout(): """ Return a pin list of all pins """ - return self.pins + return list(self.pins) def copy_layout_pin(self, instance, pin_name, new_name="", relative_offset=vector(0, 0)): """ @@ -1535,6 +1535,7 @@ class layout(): """ Return the pin shapes as blockages for non-top-level blocks. """ # FIXME: We don't have a body contact in ptx, so just ignore it for now import copy + # FIXME: this may not work now that self.pins is a dict as defined in hierarchy_spice pin_names = copy.deepcopy(self.pins) if self.name.startswith("pmos") or self.name.startswith("nmos"): pin_names.remove("B") diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 64dc0b2b..2294208e 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -13,6 +13,7 @@ from pprint import pformat from openram import debug from openram import tech from openram import OPTS +from collections import OrderedDict from .delay_data import delay_data from .wire_spice_model import wire_spice_model from .power_data import power_data @@ -49,20 +50,18 @@ class spice(): if not os.path.exists(self.lvs_file): self.lvs_file = self.sp_file - self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"] # Holds subckts/mods for this module self.mods = set() # Holds the pins for this module (in order) - self.pins = [] - # The type map of each pin: INPUT, OUTPUT, INOUT, POWER, GROUND - # for each instance, this is the set of nets/nodes that map to the pins for this instance - self.pin_type = {} + # on Python3.7+ regular dictionaries guarantee order too, but we allow use of v3.5+ + self.pins = OrderedDict() # An (optional) list of indices to reorder the pins to match the spice. self.pin_indices = [] # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # Spice format) - self.conns = [] - # If this is set, it will out output subckt or isntances of this (for row/col caps etc.) + # internal nets, which may or may not be connected to pins of the same name + self.nets = {} + # If this is set, it will not output subckt or instances of this (for row/col caps etc.) self.no_instances = False # If we are doing a trimmed netlist, these are the instance that will be filtered self.trim_insts = set() @@ -90,128 +89,114 @@ class spice(): def add_pin(self, name, pin_type="INOUT"): """ Adds a pin to the pins list. Default type is INOUT signal. """ - self.pins.append(name) - self.pin_type[name]=pin_type - debug.check(pin_type in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(name, - pin_type)) + debug.check(name not in self.pins, "cannot add duplicate spice pin {}".format(name)) + self.pins[name] = pin_spice(name, pin_type, self) def add_pin_list(self, pin_list, pin_type="INOUT"): """ Adds a pin_list to the pins list """ - # The type list can be a single type for all pins + # The pin type list can be a single type for all pins # or a list that is the same length as the pin list. - if type(pin_type)==str: + if isinstance(pin_type, str): for pin in pin_list: - debug.check(pin_type in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(pin, - pin_type)) self.add_pin(pin, pin_type) elif len(pin_type)==len(pin_list): - for (pin, ptype) in zip(pin_list, pin_type): - debug.check(ptype in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(pin, - ptype)) - self.add_pin(pin, ptype) + for (pin, type) in zip(pin_list, pin_type): + self.add_pin(pin, type) else: - debug.error("Mismatch in type and pin list lengths.", -1) + debug.error("Pin type must be a string or list of strings the same length as pin_list", -1) def add_pin_indices(self, index_list): - """ - Add pin indices for all the cell's pins. - """ + """ Add pin indices for all the cell's pins. """ self.pin_indices = index_list def get_ordered_inputs(self, input_list): - """ - Return the inputs reordered to match the pins. - """ + """ Return the inputs reordered to match the pins. """ if not self.pin_indices: return input_list new_list = [input_list[x] for x in self.pin_indices] return new_list - def add_pin_types(self, type_list): - """ - Add pin types for all the cell's pins. - """ - # This only works if self.pins == bitcell.pin_names - if len(type_list) != len(self.pins): - debug.error("{} spice subcircuit number of port types does not match number of pins\ - \n SPICE names={}\ - \n Module names={}\ - ".format(self.name, self.pins, type_list), 1) - self.pin_type = {pin: type for pin, type in zip(self.pins, type_list)} + def update_pin_types(self, type_list): + """ Change pin types for all the cell's pins. """ + debug.check(len(type_list) == len(self.pins), + "{} spice subcircuit number of port types does not match number of pins\ + \n pin names={}\n port types={}".format(self.name, list(self.pins), type_list)) + for pin, type in zip(self.pins.values(), type_list): + pin.set_type(type) def get_pin_type(self, name): """ Returns the type of the signal pin. """ - pin_type = self.pin_type[name] - debug.check(pin_type in self.valid_signal_types, - "Invalid signaltype for {0}: {1}".format(name, pin_type)) - return pin_type + pin = self.pins.get(name) + debug.check(pin is not None, "Spice pin {} not found".format(name)) + return pin.type def get_pin_dir(self, name): """ Returns the direction of the pin. (Supply/ground are INOUT). """ - if self.pin_type[name] in ["POWER", "GROUND"]: + pin_type = self.get_pin_type(name) + if pin_type in ["POWER", "GROUND"]: return "INOUT" else: - return self.pin_type[name] + return pin_type def get_inputs(self): - """ These use pin types to determine pin lists. These - may be over-ridden by submodules that didn't use pin directions yet.""" + """ + These use pin types to determine pin lists. + Returns names only, to maintain historical interface. + """ input_list = [] - for pin in self.pins: - if self.pin_type[pin]=="INPUT": - input_list.append(pin) + for pin in self.pins.values(): + if pin.type == "INPUT": + input_list.append(pin.name) return input_list def get_outputs(self): - """ These use pin types to determine pin lists. These - may be over-ridden by submodules that didn't use pin directions yet.""" + """ + These use pin types to determine pin lists. + Returns names only, to maintain historical interface. + """ output_list = [] - for pin in self.pins: - if self.pin_type[pin]=="OUTPUT": - output_list.append(pin) + for pin in self.pins.values(): + if pin.type == "OUTPUT": + output_list.append(pin.name) return output_list + def get_inouts(self): + """ + These use pin types to determine pin lists. + Returns names only, to maintain historical interface. + """ + inout_list = [] + for pin in self.pins.values(): + if pin.type == "INOUT": + inout_list.append(pin.name) + return inout_list + def copy_pins(self, other_module, suffix=""): """ This will copy all of the pins from the other module and add an optional suffix.""" - for pin in other_module.pins: - self.add_pin(pin + suffix, other_module.get_pin_type(pin)) + for pin in other_module.pins.values(): + self.add_pin(pin.name + suffix, pin.type) - def get_inouts(self): - """ These use pin types to determine pin lists. These - may be over-ridden by submodules that didn't use pin directions yet.""" - inout_list = [] - for pin in self.pins: - if self.pin_type[pin]=="INOUT": - inout_list.append(pin) - return inout_list - - def connect_inst(self, args, check=True): + def connect_inst(self, args): """ Connects the pins of the last instance added - It is preferred to use the function with the check to find if - there is a problem. The check option can be set to false - where we dynamically generate groups of connections after a - group of modules are generated. """ - num_pins = len(self.insts[-1].mod.pins) + spice_pins = list(self.insts[-1].spice_pins) + num_pins = len(spice_pins) num_args = len(args) # Order the arguments if the hard cell has a custom port order ordered_args = self.get_ordered_inputs(args) - if (check and num_pins != num_args): + if (num_pins != num_args): if num_pins < num_args: - mod_pins = self.insts[-1].mod.pins + [""] * (num_args - num_pins) + mod_pins = spice_pins + [""] * (num_args - num_pins) arg_pins = ordered_args else: arg_pins = ordered_args + [""] * (num_pins - num_args) - mod_pins = self.insts[-1].mod.pins + mod_pins = spice_pins modpins_string = "\n".join(["{0} -> {1}".format(arg, mod) for (arg, mod) in zip(arg_pins, mod_pins)]) debug.error("Connection mismatch:\nInst ({0}) -> Mod ({1})\n{2}".format(num_args, @@ -219,27 +204,17 @@ class spice(): modpins_string), 1) - self.conns.append(ordered_args) + ordered_nets = self.create_nets(ordered_args) + self.insts[-1].connect_spice_pins(ordered_nets) - # This checks if we don't have enough instance port connections for the number of insts - if check and (len(self.insts)!=len(self.conns)): - insts_string=pformat(self.insts) - conns_string=pformat(self.conns) - - debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, - len(self.insts), - len(self.conns))) - debug.error("Instances: \n" + str(insts_string)) - debug.error("-----") - debug.error("Connections: \n" + str(conns_string), 1) - - def get_conns(self, inst): - """Returns the connections of a given instance.""" - for i in range(len(self.insts)): - if inst is self.insts[i]: - return self.conns[i] - # If not found, returns None - return None + def create_nets(self, names_list): + nets = [] + for name in names_list: + # setdefault adds to the dict if it doesn't find the net in it already + # then it returns the net it found or created, a net_spice object + net = self.nets.setdefault(name, net_spice(name, self)) + nets.append(net) + return nets def sp_read(self): """ @@ -258,7 +233,7 @@ class spice(): subckt = re.compile("^.subckt {}".format(self.cell_name), re.IGNORECASE) subckt_line = list(filter(subckt.search, self.spice))[0] # parses line into ports and remove subckt - self.pins = subckt_line.split(" ")[2:] + self.add_pin_list(subckt_line.split(" ")[2:]) else: debug.info(4, "no spfile {0}".format(self.sp_file)) self.spice = [] @@ -279,10 +254,10 @@ class spice(): subckt_line = list(filter(subckt.search, self.lvs))[0] # parses line into ports and remove subckt lvs_pins = subckt_line.split(" ")[2:] - debug.check(lvs_pins == self.pins, + debug.check(lvs_pins == list(self.pins), "Spice netlists for LVS and simulation have port mismatches:\n{0} (LVS {1})\nvs\n{2} (sim {3})".format(lvs_pins, self.lvs_file, - self.pins, + list(self.pins), self.sp_file)) def check_net_in_spice(self, net_name): @@ -327,78 +302,72 @@ class spice(): # If spice isn't defined, we dynamically generate one. # recursively write the modules - for i in self.mods: - if self.contains(i, usedMODS): + for mod in self.mods: + if self.contains(mod, usedMODS): continue - usedMODS.append(i) - i.sp_write_file(sp, usedMODS, lvs, trim) + usedMODS.append(mod) + mod.sp_write_file(sp, usedMODS, lvs, trim) if len(self.insts) == 0: return - if self.pins == []: + if len(self.pins) == 0: return # write out the first spice line (the subcircuit) - wrapped_pins = "\n+ ".join(tr.wrap(" ".join(self.pins))) + wrapped_pins = "\n+ ".join(tr.wrap(" ".join(list(self.pins)))) sp.write("\n.SUBCKT {0}\n+ {1}\n".format(self.cell_name, wrapped_pins)) - # write a PININFO line - if False: - pin_info = "*.PININFO" - for pin in self.pins: - if self.pin_type[pin] == "INPUT": - pin_info += " {0}:I".format(pin) - elif self.pin_type[pin] == "OUTPUT": - pin_info += " {0}:O".format(pin) - else: - pin_info += " {0}:B".format(pin) - sp.write(pin_info + "\n") - # Also write pins as comments - for pin in self.pins: - sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin])) + for pin in self.pins.values(): + sp.write("* {1:6}: {0} \n".format(pin.name, pin.type)) for line in self.comments: sp.write("* {}\n".format(line)) - # every instance must have a set of connections, even if it is empty. - if len(self.insts) != len(self.conns): - debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.cell_name, - len(self.insts), - len(self.conns))) - debug.error("Instances: \n" + str(self.insts)) - debug.error("-----") - debug.error("Connections: \n" + str(self.conns), 1) + # every instance must be connected with the connect_inst function + # TODO: may run into empty pin lists edge case, not sure yet + connected = True + for inst in self.insts: + if inst.connected: + continue + debug.error("Instance {} spice pins not connected".format(str(inst))) + connected = False + debug.check(connected, "{0} : Not all instance spice pins are connected.".format(self.cell_name)) - for i in range(len(self.insts)): + for inst in self.insts: # we don't need to output connections of empty instances. # these are wires and paths - if self.conns[i] == []: + if len(inst.spice_pins) == 0: continue # Instance with no devices in it needs no subckt/instance - if self.insts[i].mod.no_instances: + if inst.mod.no_instances: continue # If this is a trimmed netlist, skip it by adding comment char - if trim and self.insts[i].name in self.trim_insts: + if trim and inst.name in self.trim_insts: sp.write("* ") - if lvs and hasattr(self.insts[i].mod, "lvs_device"): - sp.write(self.insts[i].mod.lvs_device.format(self.insts[i].name, - " ".join(self.conns[i]))) + if lvs and hasattr(inst.mod, "lvs_device"): + sp.write(inst.mod.lvs_device.format(inst.name, + " ".join(inst.get_connections()))) sp.write("\n") - elif hasattr(self.insts[i].mod, "spice_device"): - sp.write(self.insts[i].mod.spice_device.format(self.insts[i].name, - " ".join(self.conns[i]))) + elif hasattr(inst.mod, "spice_device"): + sp.write(inst.mod.spice_device.format(inst.name, + " ".join(inst.get_connections()))) sp.write("\n") else: - wrapped_connections = "\n+ ".join(tr.wrap(" ".join(self.conns[i]))) - - sp.write("X{0}\n+ {1}\n+ {2}\n".format(self.insts[i].name, - wrapped_connections, - self.insts[i].mod.cell_name)) + if trim and inst.name in self.trim_insts: + wrapped_connections = "\n*+ ".join(tr.wrap(" ".join(inst.get_connections()))) + sp.write("X{0}\n*+ {1}\n*+ {2}\n".format(inst.name, + wrapped_connections, + inst.mod.cell_name)) + else: + wrapped_connections = "\n+ ".join(tr.wrap(" ".join(inst.get_connections()))) + sp.write("X{0}\n+ {1}\n+ {2}\n".format(inst.name, + wrapped_connections, + inst.mod.cell_name)) sp.write(".ENDS {0}\n".format(self.cell_name)) @@ -727,6 +696,12 @@ class spice(): aliases.append(net) return aliases + def get_instance_connections(self): + conns = [] + for inst in self.insts: + conns.append(inst.get_connections()) + return conns + def is_net_alias(self, known_net, net_alias, mod, exclusion_set): """ Checks if the alias_net in input mod is the same as the input net for this mod (self). @@ -739,7 +714,7 @@ class spice(): return True # Check connections of all other subinsts mod_set = set() - for subinst, inst_conns in zip(self.insts, self.conns): + for subinst, inst_conns in zip(self.insts, self.get_instance_connections()): for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): if self.is_net_alias_name_check(known_net, inst_conn, net_alias, mod): return True @@ -756,3 +731,149 @@ class spice(): return self == mod and \ child_net.lower() == alias_net.lower() and \ parent_net.lower() == alias_net.lower() + + +class pin_spice(): + """ + A class to represent a spice netlist pin. + mod is the parent module that created this pin. + mod_net is the net object of this pin's parent module. It must have the same name as the pin. + inst is the instance this pin is a part of, if any. + inst_net is the net object from mod's nets which connects to this pin. + """ + + valid_pin_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND", "BIAS"] + + def __init__(self, name, type, mod): + self.name = name + self.set_type(type) + self.mod = mod + self.mod_net = None + self.inst = None + self.inst_net = None + + # TODO: evaluate if this makes sense... and works + self._hash = hash(self.name) + + def set_type(self, type): + debug.check(type in pin_spice.valid_pin_types, + "Invalid pin type for {0}: {1}".format(self.name, type)) + self.type = type + + def set_mod_net(self, net): + debug.check(isinstance(net, net_spice), "net must be a net_spice object") + debug.check(net.name == self.name, "module spice net must have same name as spice pin") + self.mod_net = net + + def set_inst(self, inst): + self.inst = inst + + def set_inst_net(self, net): + if self.inst_net is not None: + debug.error("pin {} is already connected to net {}\ + so it cannot also be connected to net {}\ + ".format(self.name, self.inst_net.name, net.name), 1) + debug.check(isinstance(net, net_spice), "net must be a net_spice object") + self.inst_net = net + + def __str__(self): + """ override print function output """ + return "(pin_name={} type={})".format(self.name, self.type) + + def __repr__(self): + """ override repr function output """ + return self.name + + def __eq__(self, name): + return (name == self.name) if isinstance(name, str) else super().__eq__(name) + + def __hash__(self): + """ + Implement the hash function for sets etc. + Only hash name since spice does not allow two pins to share a name. + Provides a speedup if pin_spice is used as a key for dicts. + """ + return self._hash + + def __deepcopy__(original, memo): + """ + This function is defined so that instances of modules can make deep + copies of their parent module's pins dictionary. It is only expected + to be called by the instance class __init__ function. Mod and mod_net + should not be deep copies but references to the existing mod and net + objects they refer to in the original. If inst is already defined this + function will throw an error because that means it was called on a pin + from an instance, which is not defined behavior. + """ + debug.check(original.inst is None, + "cannot make a deepcopy of a spice pin from an inst") + pin = pin_spice(original.name, original.type, original.mod) + if original.mod_net is not None: + pin.set_mod_net(original.mod_net) + return pin + + +class net_spice(): + """ + A class to represent a spice net. + mod is the parent module that created this net. + pins are all the pins connected to this net. + inst is the instance this net is a part of, if any. + """ + + def __init__(self, name, mod): + self.name = name + self.pins = [] + self.mod = mod + self.inst = None + + # TODO: evaluate if this makes sense... and works + self._hash = hash(self.name) + + def connect_pin(self, pin): + debug.check(isinstance(pin, pin_spice), "pin must be a pin_spice object") + if pin in self.pins: + debug.warning("pin {} was already connected to net {} ... why was it connected again?".format(pin.name, self.name)) + else: + self.pins.append(pin) + + def set_inst(self, inst): + self.inst = inst + + def __str__(self): + """ override print function output """ + return "(net_name={} type={})".format(self.name, self.type) + + def __repr__(self): + """ override repr function output """ + return self.name + + def __eq__(self, name): + return (name == self.name) if isinstance(name, str) else super().__eq__(name) + + def __hash__(self): + """ + Implement the hash function for sets etc. + Only hash name since spice does not allow two nets to share a name + (on the same level of hierarchy, or rather they will be the same net). + Provides a speedup if net_spice is used as a key for dicts. + """ + return self._hash + + def __deepcopy__(original, memo): + """ + This function is defined so that instances of modules can make deep + copies of their parent module's nets dictionary. It is only expected + to be called by the instance class __init__ function. Mod + should not be a deep copy but a reference to the existing mod + object it refers to in the original. If inst is already defined this + function will throw an error because that means it was called on a net + from an instance, which is not defined behavior. + """ + debug.check(original.inst is None, + "cannot make a deepcopy of a spice net from an inst") + net = net_spice(original.name, original.mod) + if original.pins != []: + # TODO: honestly I'm not sure if this is right but we'll see... + net.pins = original.pins + return net diff --git a/compiler/base/lef.py b/compiler/base/lef.py index b138799b..e08f0efc 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -18,7 +18,7 @@ class lef: """ SRAM LEF Class open GDS file, read pins information, obstruction and write them to LEF file. - This is inherited by the sram_base class. + This is inherited by the sram_1bank class. """ def __init__(self, layers): # LEF db units per micron diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 0920d4ea..9913173e 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -19,11 +19,12 @@ from .simulation import * from .measurements import * from .model_check import * from .analytical_util import * +from .fake_sram import * debug.info(1, "Initializing characterizer...") OPTS.spice_exe = "" -if not OPTS.analytical_delay: +if not OPTS.analytical_delay or OPTS.top_process in ["memfunc", "memchar"]: if OPTS.spice_name: # Capitalize Xyce if OPTS.spice_name == "xyce": diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py index 872901b7..1c365fd5 100644 --- a/compiler/characterizer/charutils.py +++ b/compiler/characterizer/charutils.py @@ -7,6 +7,7 @@ # import os import re +from enum import Enum from openram import debug from openram import OPTS @@ -107,3 +108,33 @@ def check_dict_values_is_float(dict): if type(value)!=float: return False return True + + +def bidir_search(func, upper, lower, time_out=9): + """ + Performs bidirectional search over given function with given + upper and lower bounds. + """ + time_count = 0 + while time_count < time_out: + val = (upper + lower) / 2 + if func(val): + return (True, val) + time_count += 1 + return (False, 0) + + +class bit_polarity(Enum): + NONINVERTING = 0 + INVERTING = 1 + + +class sram_op(Enum): + READ_ZERO = 0 + READ_ONE = 1 + WRITE_ZERO = 2 + WRITE_ONE = 3 + DISABLED_READ_ZERO = 4 + DISABLED_READ_ONE = 5 + DISABLED_WRITE_ZERO = 6 + DISABLED_WRITE_ONE = 7 diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 90ebf80d..cbf4f7a6 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -13,10 +13,10 @@ from openram import OPTS from .stimuli import * from .trim_spice import * from .charutils import * -from .sram_op import * -from .bit_polarity import * from .simulation import simulation from .measurements import * +from os import path +import re class delay(simulation): @@ -37,7 +37,7 @@ class delay(simulation): """ - def __init__(self, sram, spfile, corner): + def __init__(self, sram, spfile, corner, output_path=None): super().__init__(sram, spfile, corner) self.targ_read_ports = [] @@ -47,10 +47,17 @@ class delay(simulation): self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) else: self.num_wmasks = 0 + + if output_path is None: + self.output_path = OPTS.openram_temp + else: + self.output_path = output_path + self.set_load_slew(0, 0) self.set_corner(corner) self.create_signal_names() self.add_graph_exclusions() + self.meas_id = 0 def create_measurement_objects(self): """ Create the measurements used for read and write ports """ @@ -79,10 +86,12 @@ class delay(simulation): self.clk_frmt = "clk{0}" # Unformatted clock name targ_name = "{0}{{}}_{1}".format(self.dout_name, self.probe_data) # Empty values are the port and probe data bit self.delay_meas = [] - self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "RISE", "RISE", measure_scale=1e9)) + self.delay_meas.append(delay_measure("delay_lh", self.clk_frmt, targ_name, "FALL", "RISE", measure_scale=1e9)) self.delay_meas[-1].meta_str = sram_op.READ_ONE # Used to index time delay values when measurements written to spice file. + self.delay_meas[-1].meta_add_delay = False self.delay_meas.append(delay_measure("delay_hl", self.clk_frmt, targ_name, "FALL", "FALL", measure_scale=1e9)) self.delay_meas[-1].meta_str = sram_op.READ_ZERO + self.delay_meas[-1].meta_add_delay = False self.read_lib_meas+=self.delay_meas self.slew_meas = [] @@ -103,9 +112,10 @@ class delay(simulation): self.read_lib_meas[-1].meta_str = "disabled_read0" # This will later add a half-period to the spice time delay. Only for reading 0. - for obj in self.read_lib_meas: - if obj.meta_str is sram_op.READ_ZERO: - obj.meta_add_delay = True + # FIXME: Removed this to check, see if it affects anything + #for obj in self.read_lib_meas: + # if obj.meta_str is sram_op.READ_ZERO: + # obj.meta_add_delay = True read_measures = [] read_measures.append(self.read_lib_meas) @@ -113,7 +123,9 @@ class delay(simulation): read_measures.append(self.create_bitline_measurement_objects()) read_measures.append(self.create_debug_measurement_objects()) read_measures.append(self.create_read_bit_measures()) - read_measures.append(self.create_sen_and_bitline_path_measures()) + # TODO: Maybe don't do this here (?) + if OPTS.top_process != "memchar": + read_measures.append(self.create_sen_and_bitline_path_measures()) return read_measures @@ -158,6 +170,7 @@ class delay(simulation): write_measures = [] write_measures.append(self.write_lib_meas) write_measures.append(self.create_write_bit_measures()) + return write_measures def create_debug_measurement_objects(self): @@ -216,8 +229,12 @@ class delay(simulation): bit_col = self.get_data_bit_column_number(probe_address, probe_data) bit_row = self.get_address_row_number(probe_address) - (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col) - storage_names = cell_inst.mod.get_storage_net_names() + if OPTS.top_process == "memchar": + cell_name = self.cell_name.format(bit_row, bit_col) + storage_names = ("Q", "Q_bar") + else: + (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, bit_row, bit_col) + storage_names = cell_inst.mod.get_storage_net_names() debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" "supported for characterization. Storage nets={0}").format(storage_names)) if OPTS.use_pex and OPTS.pex_exe[0] != "calibre": @@ -239,8 +256,8 @@ class delay(simulation): def create_sen_and_bitline_path_measures(self): """Create measurements for the s_en and bitline paths for individual delays per stage.""" - # FIXME: There should be a default_read_port variable in this case, pathing is done with this - # but is never mentioned otherwise +# # FIXME: There should be a default_read_port variable in this case, pathing is done with this +# # but is never mentioned otherwise port = self.read_ports[0] sen_and_port = self.sen_name + str(port) bl_and_port = self.bl_name.format(port) # bl_name contains a '{}' for the port @@ -255,8 +272,8 @@ class delay(simulation): bitline_path = bl_paths[0] # Get the measures - self.sen_path_meas = self.create_delay_path_measures(sen_path) - self.bl_path_meas = self.create_delay_path_measures(bitline_path) + self.sen_path_meas = self.create_delay_path_measures(sen_path, "sen") + self.bl_path_meas = self.create_delay_path_measures(bitline_path, "bl") all_meas = self.sen_path_meas + self.bl_path_meas # Paths could have duplicate measurements, remove them before they go to the stim file @@ -278,7 +295,7 @@ class delay(simulation): return unique_measures - def create_delay_path_measures(self, path): + def create_delay_path_measures(self, path, process): """Creates measurements for each net along given path.""" # Determine the directions (RISE/FALL) of signals @@ -290,6 +307,8 @@ class delay(simulation): cur_net, next_net = path[i], path[i + 1] cur_dir, next_dir = path_dirs[i], path_dirs[i + 1] meas_name = "delay_{0}_to_{1}".format(cur_net, next_net) + meas_name += "_" + process + "_id" + str(self.meas_id) + self.meas_id += 1 if i + 1 != len(path) - 1: path_meas.append(delay_measure(meas_name, cur_net, next_net, cur_dir, next_dir, measure_scale=1e9, has_port=False)) else: # Make the last measurement always measure on FALL because is a read 0 @@ -368,7 +387,7 @@ class delay(simulation): self.sf.write("\n* SRAM output loads\n") for port in self.read_ports: - for i in range(self.word_size): + for i in range(self.word_size + self.num_spare_cols): self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port, i, self.dout_name, self.load)) def write_delay_stimulus(self): @@ -385,15 +404,20 @@ class delay(simulation): # creates and opens stimulus file for writing self.delay_stim_sp = "delay_stim.sp" - temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.delay_stim_sp) + temp_stim = path.join(self.output_path, self.delay_stim_sp) self.sf = open(temp_stim, "w") + # creates and opens measure file for writing + self.delay_meas_sp = "delay_meas.sp" + temp_meas = path.join(self.output_path, self.delay_meas_sp) + self.mf = open(temp_meas, "w") + if OPTS.spice_name == "spectre": self.sf.write("simulator lang=spice\n") self.sf.write("* Delay stimulus for period of {0}n load={1}fF slew={2}ns\n\n".format(self.period, self.load, self.slew)) - self.stim = stimuli(self.sf, self.corner) + self.stim = stimuli(self.sf, self.mf, self.corner) # include files in stimulus file self.stim.write_include(self.trim_sp_file) @@ -418,6 +442,7 @@ class delay(simulation): t_rise=self.slew, t_fall=self.slew) + self.sf.write(".include {0}".format(temp_meas)) # self.load_all_measure_nets() self.write_delay_measures() # self.write_simulation_saves() @@ -426,6 +451,7 @@ class delay(simulation): self.stim.write_control(self.cycle_times[-1] + self.period) self.sf.close() + self.mf.close() def write_power_stimulus(self, trim): """ Creates a stimulus file to measure leakage power only. @@ -435,10 +461,15 @@ class delay(simulation): # creates and opens stimulus file for writing self.power_stim_sp = "power_stim.sp" - temp_stim = "{0}/{1}".format(OPTS.openram_temp, self.power_stim_sp) + temp_stim = path.join(self.output_path, self.power_stim_sp) self.sf = open(temp_stim, "w") self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period)) - self.stim = stimuli(self.sf, self.corner) + + # creates and opens measure file for writing + self.power_meas_sp = "power_meas.sp" + temp_meas = path.join(self.output_path, self.power_meas_sp) + self.mf = open(temp_meas, "w") + self.stim = stimuli(self.sf, self.mf, self.corner) # include UNTRIMMED files in stimulus file if trim: @@ -451,7 +482,7 @@ class delay(simulation): # generate data and addr signals self.sf.write("\n* Generation of data and address signals\n") for write_port in self.write_ports: - for i in range(self.word_size): + for i in range(self.word_size + self.num_spare_cols): self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i), v_val=0) for port in self.all_ports: @@ -470,12 +501,14 @@ class delay(simulation): for port in self.all_ports: self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) + self.sf.write(".include {}".format(temp_meas)) self.write_power_measures() # run until the end of the cycle time self.stim.write_control(2 * self.period) self.sf.close() + self.mf.close() def get_measure_variants(self, port, measure_obj, measure_type=None): """ @@ -587,15 +620,15 @@ class delay(simulation): # Output some comments to aid where cycles start and # what is happening for comment in self.cycle_comments: - self.sf.write("* {0}\n".format(comment)) + self.mf.write("* {0}\n".format(comment)) self.sf.write("\n") for read_port in self.targ_read_ports: - self.sf.write("* Read ports {0}\n".format(read_port)) + self.mf.write("* Read ports {0}\n".format(read_port)) self.write_delay_measures_read_port(read_port) for write_port in self.targ_write_ports: - self.sf.write("* Write ports {0}\n".format(write_port)) + self.mf.write("* Write ports {0}\n".format(write_port)) self.write_delay_measures_write_port(write_port) def load_pex_net(self, net: str): @@ -605,8 +638,8 @@ class delay(simulation): return net original_net = net net = net[len(prefix):] - net = net.replace(".", "_").replace("[", "\[").replace("]", "\]") - for pattern in ["\sN_{}_[MXmx]\S+_[gsd]".format(net), net]: + net = net.replace(".", "_").replace("[", r"\[").replace("]", r"\]") + for pattern in [r"\sN_{}_[MXmx]\S+_[gsd]".format(net), net]: try: match = check_output(["grep", "-m1", "-o", "-iE", pattern, self.sp_file]) return prefix + match.decode().strip() @@ -616,8 +649,8 @@ class delay(simulation): def load_all_measure_nets(self): measurement_nets = set() - for port, meas in zip(self.targ_read_ports * len(self.read_meas_lists) + - self.targ_write_ports * len(self.write_meas_lists), + for port, meas in zip(self.targ_read_ports * len(self.read_meas_lists) + + self.targ_write_ports * len(self.write_meas_lists), self.read_meas_lists + self.write_meas_lists): for measurement in meas: visited = getattr(measurement, 'pex_visited', False) @@ -684,6 +717,8 @@ class delay(simulation): self.sf.write("\n* Measure statements for idle leakage power\n") # add measure statements for power + # TODO: Convert to measure statement insted of using stimuli + # measure = power_measure('leakage_power', t_initial = self.period t_final = 2 * self.period self.stim.gen_meas_power(meas_name="leakage_power", @@ -791,7 +826,7 @@ class delay(simulation): for port in self.targ_write_ports: if not self.check_bit_measures(self.write_bit_meas, port): - return(False, {}) + return (False, {}) debug.info(2, "Checking write values for port {0}".format(port)) write_port_dict = {} @@ -805,7 +840,7 @@ class delay(simulation): for port in self.targ_read_ports: # First, check that the memory has the right values at the right times if not self.check_bit_measures(self.read_bit_meas, port): - return(False, {}) + return (False, {}) debug.info(2, "Checking read delay values for port {0}".format(port)) # Check sen timing, then bitlines, then general measurements. @@ -828,7 +863,8 @@ class delay(simulation): result[port].update(read_port_dict) - self.path_delays = self.check_path_measures() + if self.sen_path_meas and self.bl_path_meas: + self.path_delays = self.check_path_measures() return (True, result) @@ -919,7 +955,7 @@ class delay(simulation): def check_bitline_meas(self, v_discharged_bl, v_charged_bl): """ Checks the value of the discharging bitline. Confirms s_en timing errors. - Returns true if the bitlines are at there expected value. + Returns true if the bitlines are at there their value. """ # The inputs looks at discharge/charged bitline rather than left or right (bl/br) # Performs two checks, discharging bitline is at least 10% away from vdd and there is a @@ -942,7 +978,7 @@ class delay(simulation): if type(val) != float or val > self.period / 2: debug.info(1, 'Failed measurement:{}={}'.format(meas.name, val)) value_dict[meas.name] = val - #debug.info(0, "value_dict={}".format(value_dict)) + # debug.info(0, "value_dict={}".format(value_dict)) return value_dict def run_power_simulation(self): @@ -992,8 +1028,8 @@ class delay(simulation): slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl, slew_lh) # high-to-low delays start at neg. clk edge, so they need to be less than half_period half_period = self.period / 2 - if abs(delay_hl)>half_period or abs(delay_lh)>self.period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \ - or delay_hl<0 or delay_lh<0 or slew_hl<0 or slew_lh<0: + if abs(delay_hl)>half_period or abs(delay_lh)>half_period or abs(slew_hl)>half_period or abs(slew_lh)>self.period \ + or (delay_hl<0 and delay_lh<0) or slew_hl<0 or slew_lh<0: debug.info(2, "UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str, delays_str, slews_str)) @@ -1003,6 +1039,13 @@ class delay(simulation): delays_str, slews_str)) + if delay_lh < 0 and delay_hl > 0: + result_dict["delay_lh"] = result_dict["delay_hl"] + debug.info(2, "delay_lh captured precharge, using delay_hl instead") + elif delay_hl < 0 and delay_lh > 0: + result_dict["delay_hl"] = result_dict["delay_lh"] + debug.info(2, "delay_hl captured precharge, using delay_lh instead") + return True def find_min_period(self, feasible_delays): @@ -1108,32 +1151,143 @@ class delay(simulation): Netlist reduced for simulation. """ super().set_probe(probe_address, probe_data) - self.prepare_netlist() def prepare_netlist(self): """ Prepare a trimmed netlist and regular netlist. """ # Set up to trim the netlist here if that is enabled + # TODO: Copy old netlist if memchar if OPTS.trim_netlist: + #self.trim_sp_file = "{0}trimmed.sp".format(self.output_path) self.trim_sp_file = "{0}trimmed.sp".format(OPTS.openram_temp) - self.sram.sp_write(self.trim_sp_file, lvs=False, trim=True) + # Only genrate spice when running openram process + if OPTS.top_process != "memchar": + self.sram.sp_write(self.trim_sp_file, lvs=False, trim=True) else: # The non-reduced netlist file when it is disabled - self.trim_sp_file = "{0}sram.sp".format(OPTS.openram_temp) + self.trim_sp_file = "{0}sram.sp".format(self.output_path) # The non-reduced netlist file for power simulation - self.sim_sp_file = "{0}sram.sp".format(OPTS.openram_temp) + self.sim_sp_file = "{0}sram.sp".format(self.output_path) # Make a copy in temp for debugging - shutil.copy(self.sp_file, self.sim_sp_file) + if self.sp_file != self.sim_sp_file: + shutil.copy(self.sp_file, self.sim_sp_file) + + def recover_measurment_objects(self): + mf_path = path.join(OPTS.output_path, "delay_meas.sp") + self.sen_path_meas = None + self.bl_path_meas = None + if not path.exists(mf_path): + debug.info(1, "Delay measure file not found. Skipping measure recovery") + return + mf = open(mf_path, "r") + measure_text = mf.read() + port_iter = re.finditer(r"\* (Read|Write) ports (\d*)", measure_text) + port_measure_lines = [] + loc = 0 + port_name = '' + for port in port_iter: + port_measure_lines.append((port_name, measure_text[loc:port.end(0)])) + loc = port.start(0) + port_name = port.group(1) + port.group(2) + + mf.close() + # Cycle comments, not sure if i need this + # cycle_lines = port_measure_lines.pop(0)[1] + # For now just recover the bit_measures and sen_and_bitline_path_measures + self.read_meas_lists.append([]) + self.read_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} + self.write_bit_meas = {bit_polarity.NONINVERTING: [], bit_polarity.INVERTING: []} +# bit_measure_rule = re.compile(r"\.meas tran (v_q_a\d+_b\d+_(read|write)_(zero|one)\d+) FIND v\((.*)\) AT=(\d+(\.\d+)?)n") +# for measures in port_measure_lines: +# port_name = measures[0] +# text = measures[1] +# bit_measure_iter = bit_measure_rule.finditer(text) +# for bit_measure in bit_measure_iter: +# meas_name = bit_measure.group(1) +# read = bit_measure.group(2) == "read" +# cycle = bit_measure.group(3) +# probe = bit_measure.group(4) +# polarity = bit_polarity.NONINVERTING +# if "q_bar" in meas_name: +# polarity = bit_polarity.INVERTING +# meas = voltage_at_measure(meas_name, probe) +# if read: +# if cycle == "one": +# meas.meta_str = sram_op.READ_ONE +# else: +# meas.meta_str = sram_op.READ_ZERO +# self.read_bit_meas[polarity].append(meas) +# self.read_meas_lists[-1].append(meas) +# else: +# if cycle == "one": +# meas.meta_str = sram_op.WRITE_ONE +# else: +# meas.meta_str = sram_op.WRITE_ZERO +# self.write_bit_meas[polarity].append(meas) +# self.write_meas_lists[-1].append(meas) + + delay_path_rule = re.compile(r"\.meas tran delay_(.*)_to_(.*)_(sen|bl)_(id\d*) TRIG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n TARG v\((.*)\) VAL=(\d+(\.\d+)?) (RISE|FALL)=(\d+) TD=(\d+(\.\d+)?)n") + port = self.read_ports[0] + meas_buff = [] + self.sen_path_meas = [] + self.bl_path_meas = [] + for measures in port_measure_lines: + text = measures[1] + delay_path_iter = delay_path_rule.finditer(text) + for delay_path_measure in delay_path_iter: + from_ = delay_path_measure.group(1) + to_ = delay_path_measure.group(2) + path_ = delay_path_measure.group(3) + id_ = delay_path_measure.group(4) + trig_rise = delay_path_measure.group(8) + targ_rise = delay_path_measure.group(15) + meas_name = "delay_{0}_to_{1}_{2}_{3}".format(from_, to_, path_, id_) + meas = delay_measure(meas_name, from_, to_, trig_rise, targ_rise, measure_scale=1e9, has_port=False) + meas.meta_str = sram_op.READ_ZERO + meas.meta_add_delay = True + meas_buff.append(meas) + if path_ == "sen": + self.sen_path_meas.extend(meas_buff.copy()) + meas_buff.clear() + elif path_ == "bl": + self.bl_path_meas.extend(meas_buff.copy()) + meas_buff.clear() + self.read_meas_lists.append(self.sen_path_meas + self.bl_path_meas) + + def set_spice_names(self): + """This is run in place of set_internal_spice_names function from + simulation.py when running stand-alone characterizer.""" + self.bl_name = OPTS.bl_format.format(name=self.sram.name, + hier_sep=OPTS.hier_seperator, + row="{}", + col=self.bitline_column) + self.br_name = OPTS.br_format.format(name=self.sram.name, + hier_sep=OPTS.hier_seperator, + row="{}", + col=self.bitline_column) + self.sen_name = OPTS.sen_format.format(name=self.sram.name, + hier_sep=OPTS.hier_seperator) + self.cell_name = OPTS.cell_format.format(name=self.sram.name, + hier_sep=OPTS.hier_seperator, + row="{}", + col="{}") def analysis_init(self, probe_address, probe_data): """Sets values which are dependent on the data address/bit being tested.""" self.set_probe(probe_address, probe_data) - self.create_graph() - self.set_internal_spice_names() - self.create_measurement_names() - self.create_measurement_objects() + self.prepare_netlist() + if OPTS.top_process == "memchar": + self.set_spice_names() + self.create_measurement_names() + self.create_measurement_objects() + self.recover_measurment_objects() + else: + self.create_graph() + self.set_internal_spice_names() + self.create_measurement_names() + self.create_measurement_objects() def analyze(self, probe_address, probe_data, load_slews): """ @@ -1145,7 +1299,7 @@ class delay(simulation): self.analysis_init(probe_address, probe_data) loads = [] slews = [] - for load,slew in load_slews: + for load, slew in load_slews: loads.append(load) slews.append(slew) self.load=max(loads) @@ -1168,15 +1322,16 @@ class delay(simulation): # 4) At the minimum period, measure the delay, slew and power for all slew/load pairs. self.period = min_period char_port_data = self.simulate_loads_and_slews(load_slews, leakage_offset) - if OPTS.use_specified_load_slew != None and len(load_slews) > 1: + if OPTS.use_specified_load_slew is not None and len(load_slews) > 1: debug.warning("Path delay lists not correctly generated for characterizations of more than 1 load,slew") # Get and save the path delays - bl_names, bl_delays, sen_names, sen_delays = self.get_delay_lists(self.path_delays) + if self.sen_path_meas and self.bl_path_meas: + bl_names, bl_delays, sen_names, sen_delays = self.get_delay_lists(self.path_delays) # Removed from characterization output temporarily - #char_sram_data["bl_path_measures"] = bl_delays - #char_sram_data["sen_path_measures"] = sen_delays - #char_sram_data["bl_path_names"] = bl_names - #char_sram_data["sen_path_names"] = sen_names + # char_sram_data["bl_path_measures"] = bl_delays + # char_sram_data["sen_path_measures"] = sen_delays + # char_sram_data["bl_path_names"] = bl_names + # char_sram_data["sen_path_names"] = sen_names # FIXME: low-to-high delays are altered to be independent of the period. This makes the lib results less accurate. self.alter_lh_char_data(char_port_data) @@ -1185,7 +1340,7 @@ class delay(simulation): def alter_lh_char_data(self, char_port_data): """Copies high-to-low data to low-to-high data to make them consistent on the same clock edge.""" - # This is basically a hack solution which should be removed/fixed later. + # This is basically a hack solution which should be removed/fixed later. for port in self.all_ports: char_port_data[port]['delay_lh'] = char_port_data[port]['delay_hl'] char_port_data[port]['slew_lh'] = char_port_data[port]['slew_hl'] @@ -1194,7 +1349,6 @@ class delay(simulation): """Simulate all specified output loads and input slews pairs of all ports""" measure_data = self.get_empty_measure_data_dict() - path_dict = {} # Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. self.targ_read_ports = self.read_ports self.targ_write_ports = self.write_ports @@ -1255,8 +1409,8 @@ class delay(simulation): inverse_address = self.calculate_inverse_address() # For now, ignore data patterns and write ones or zeros - data_ones = "1" * self.word_size - data_zeros = "0" * self.word_size + data_ones = "1" * (self.word_size + self.num_spare_cols) + data_zeros = "0" * (self.word_size + self.num_spare_cols) wmask_ones = "1" * self.num_wmasks if self.t_current == 0: @@ -1352,9 +1506,9 @@ class delay(simulation): # Get any available read/write port in case only a single write or read ports is being characterized. cur_read_port = self.get_available_port(get_read_port=True) cur_write_port = self.get_available_port(get_read_port=False) - debug.check(cur_read_port != None, + debug.check(cur_read_port is not None, "Characterizer requires at least 1 read port") - debug.check(cur_write_port != None, + debug.check(cur_write_port is not None, "Characterizer requires at least 1 write port") # Create test cycles for specified target ports. @@ -1380,7 +1534,7 @@ class delay(simulation): """ Generates the PWL data inputs for a simulation timing test. """ for write_port in self.write_ports: - for i in range(self.word_size): + for i in range(self.word_size + self.num_spare_cols): sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) @@ -1402,3 +1556,6 @@ class delay(simulation): self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) if port in self.readwrite_ports: self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) + if self.sram.num_wmasks: + for bit in range(self.sram.num_wmasks): + self.stim.gen_pwl("WMASK{0}_{1}".format(port, bit), self.cycle_times, self.wmask_values[port][bit], self.period, self.slew, 0.05) diff --git a/compiler/characterizer/fake_sram.py b/compiler/characterizer/fake_sram.py new file mode 100644 index 00000000..4dfa2f56 --- /dev/null +++ b/compiler/characterizer/fake_sram.py @@ -0,0 +1,108 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from openram import sram_config +from math import ceil +from openram import OPTS + + +class fake_sram(sram_config): + """ + This is an SRAM class that doesn't actually create an instance. + It will read neccessary members from HTML file from a previous run. + """ + def __init__(self, name, word_size, num_words, write_size=None, num_banks=1, + words_per_row=None, num_spare_rows=0, num_spare_cols=0): + sram_config.__init__(self, word_size, num_words, write_size, + num_banks, words_per_row, num_spare_rows, + num_spare_cols) + self.name = name + if write_size and self.write_size != self.word_size: + self.num_wmasks = int(ceil(self.word_size / self.write_size)) + else: + self.num_wmasks = 0 + + def setup_multiport_constants(self): + """ + Taken from ../base/design.py + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are the write-only port indices. + self.writeonly_ports = [] + # These are the read/write and read-only port indices + self.read_ports = [] + # These are the read-only port indices. + self.readonly_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) + + # The order is always fixed as RW, W, R + port_number = 0 + for port in range(OPTS.num_rw_ports): + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_w_ports): + self.write_ports.append(port_number) + self.writeonly_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_r_ports): + self.read_ports.append(port_number) + self.readonly_ports.append(port_number) + port_number += 1 + + def generate_pins(self): + self.pins = ['vdd', 'gnd'] + self.pins.extend(['clk{}'.format(port) for port in range( + OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports)]) + for port in range(OPTS.num_rw_ports): + self.pins.extend(['din{0}[{1}]'.format(port, bit) + for bit in range(self.word_size + self.num_spare_cols)]) + self.pins.extend(['dout{0}[{1}]'.format(port, bit) + for bit in range(self.word_size + self.num_spare_cols)]) + self.pins.extend(['addr{0}[{1}]'.format(port, bit) + for bit in range(self.addr_size)]) + self.pins.extend(['spare_wen{0}[{1}]'.format(port, bit) + for bit in range(self.num_spare_cols)]) + if self.num_wmasks != 0: + self.pins.extend(['wmask{0}[{1}]'.format(port, bit) + for bit in range(self.num_wmasks)]) + + self.pins.extend(['csb{}'.format(port), 'web{}'.format(port)]) + + start_port = OPTS.num_rw_ports + for port in range(start_port, start_port + OPTS.num_r_ports): + self.pins.extend(['dout{0}[{1}]'.format(port, bit) + for bit in range(self.word_size + self.num_spare_cols)]) + self.pins.extend(['addr{0}[{1}]'.format(port, bit) + for bit in range(self.addr_size)]) + + self.pins.extend(['csb{}'.format(port)]) + + start_port += OPTS.num_r_ports + for port in range(start_port, start_port + OPTS.num_w_ports): + self.pins.extend(['din{0}[{1}]'.format(port, bit) + for bit in range(self.word_size + self.num_spare_cols)]) + self.pins.extend(['spare_wen{0}[{1}]'.format(port, bit) + for bit in range(self.num_spare_cols)]) + self.pins.extend(['addr{0}[{1}]'.format(port, bit) + for bit in range(self.addr_size)]) + if self.num_wmasks != 0: + self.pins.extend(['wmask{0}[{1}]'.format(port, bit) + for bit in range(self.num_wmasks)]) + + self.pins.extend(['csb{}'.format(port), 'web{}'.format(port)]) diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index ddf0d588..49dba9da 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -7,13 +7,17 @@ # import math import random +import time import collections +from os import path +import shutil from numpy import binary_repr from openram import debug from openram import OPTS from .stimuli import * from .charutils import * from .simulation import simulation +from .measurements import voltage_at_measure class functional(simulation): @@ -28,17 +32,24 @@ class functional(simulation): # Seed the characterizer with a constant seed for unit tests if OPTS.is_unit_test: random.seed(12345) + elif OPTS.functional_seed: + random.seed(OPTS.functional_seed) + else: + seed = time.time_ns() + random.seed(seed) + debug.info(1, "Random seed for functional simulation: {}".format(seed)) + if not spfile: # self.sp_file is assigned in base class sram.sp_write(self.sp_file, trim=OPTS.trim_netlist) + # Copy sp file to temp dir + self.temp_spice = path.join(OPTS.openram_temp, "sram.sp") + shutil.copy(self.sp_file, self.temp_spice) if not corner: corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - if period: - self.period = period - if not output_path: self.output_path = OPTS.openram_temp else: @@ -73,13 +84,20 @@ class functional(simulation): self.set_spice_constants() self.set_stimulus_variables() + # Override default period + if period: + self.period = period + # For the debug signal names self.wordline_row = 0 self.bitline_column = 0 self.create_signal_names() - self.add_graph_exclusions() - self.create_graph() - self.set_internal_spice_names() + #self.add_graph_exclusions() + #self.create_graph() + #self.set_internal_spice_names() + self.bl_name = "xsram:xbank0:bl_0_{}" + self.br_name = "xsram:xbank0:br_0_{}" + self.sen_name = "xsram:s_en" self.q_name, self.qbar_name = self.get_bit_name() debug.info(2, "q:\t\t{0}".format(self.q_name)) debug.info(2, "qbar:\t{0}".format(self.qbar_name)) @@ -145,7 +163,6 @@ class functional(simulation): comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.bank_addr_size, "0" * self.num_wmasks, 0, self.t_current) self.add_noop_all_ports(comment) - # 1. Write all the write ports 2x to seed a bunch of locations. for i in range(3): for port in self.write_ports: @@ -267,7 +284,7 @@ class functional(simulation): self.read_check.append([word, "{0}{1}".format(self.dout_name, port), self.t_current + self.period, - int(self.t_current/self.period)]) + int(self.t_current / self.period)]) def read_stim_results(self): # Extract dout values from spice timing.lis @@ -275,7 +292,8 @@ class functional(simulation): sp_read_value = "" for bit in range(self.word_size + self.num_spare_cols): measure_name = "v{0}_{1}ck{2}".format(dout_port.lower(), bit, cycle) - value = parse_spice_list("timing", measure_name) + # value = parse_spice_list("timing", measure_name) + value = self.measures[measure_name].retrieve_measure(port=0) # FIXME: Ignore the spare columns for now if bit >= self.word_size: value = 0 @@ -294,10 +312,11 @@ class functional(simulation): self.v_low, self.v_high) except ValueError: - error ="FAILED: {0}_{1} value {2} at time {3}n is not a float.".format(dout_port, + error ="FAILED: {0}_{1} value {2} at time {3}n is not a float. Measure: {4}".format(dout_port, bit, value, - eo_period) + eo_period, + measure_name) return (0, error) self.read_results.append([sp_read_value, dout_port, eo_period, cycle]) @@ -318,8 +337,8 @@ class functional(simulation): cycle, self.read_results[i][2], check_name) - return(0, error) - return(1, "SUCCESS") + return (0, error) + return (1, "SUCCESS") def gen_wmask(self): wmask = "" @@ -347,7 +366,7 @@ class functional(simulation): def gen_data(self): """ Generates a random word to write. """ # Don't use 0 or max value - random_value = random.randint(1, self.max_data - 1) + random_value = random.randint(1, self.max_data) data_bits = binary_repr(random_value, self.word_size) if self.num_spare_cols>0: random_value = random.randint(0, self.max_col_data) @@ -380,13 +399,16 @@ class functional(simulation): def write_functional_stimulus(self): """ Writes SPICE stimulus. """ self.stim_sp = "functional_stim.sp" - temp_stim = "{0}/{1}".format(self.output_path, self.stim_sp) + temp_stim = path.join(self.output_path, self.stim_sp) self.sf = open(temp_stim, "w") self.sf.write("* Functional test stimulus file for {0}ns period\n\n".format(self.period)) - self.stim = stimuli(self.sf, self.corner) + self.meas_sp = "functional_meas.sp" + temp_meas = path.join(self.output_path, self.meas_sp) + self.mf = open(temp_meas, "w") + self.stim = stimuli(self.sf, self.mf, self.corner) # Write include statements - self.stim.write_include(self.sp_file) + self.stim.write_include(self.temp_spice) # Write Vdd/Gnd statements self.sf.write("\n* Global Power Supplies\n") @@ -470,6 +492,7 @@ class functional(simulation): # Generate dout value measurements self.sf.write("\n * Generation of dout measurements\n") + self.measures = {} for (word, dout_port, eo_period, cycle) in self.read_check: t_initial = eo_period @@ -477,28 +500,37 @@ class functional(simulation): num_bits = self.word_size + self.num_spare_cols for bit in range(num_bits): signal_name = "{0}_{1}".format(dout_port, bit) - measure_name = "V{0}ck{1}".format(signal_name, cycle) + measure_name = "v{0}ck{1}".format(signal_name, cycle) voltage_value = self.stim.get_voltage(word[num_bits - bit - 1]) self.stim.add_comment("* CHECK {0} {1} = {2} time = {3}".format(signal_name, measure_name, voltage_value, eo_period)) - self.stim.gen_meas_value(meas_name=measure_name, - dout=signal_name, - t_initial=t_initial, - t_final=t_final) + # TODO: Convert to measurement statement instead of stimuli + meas = voltage_at_measure(measure_name, signal_name) + self.measures[measure_name] = meas + meas.write_measure(self.stim, ((t_initial + t_final) / 2, 0)) + # self.stim.gen_meas_value(meas_name=measure_name, + # dout=signal_name, + # t_initial=t_initial, + # t_final=t_final + self.sf.write(".include {0}\n".format(temp_meas)) self.stim.write_control(self.cycle_times[-1] + self.period) self.sf.close() + self.mf.close() # FIXME: Similar function to delay.py, refactor this def get_bit_name(self): """ Get a bit cell name """ - (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0) - storage_names = cell_inst.mod.get_storage_net_names() - debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" - "supported for characterization. Storage nets={0}").format(storage_names)) + # TODO: Find a way to get the cell_name and storage_names statically + # (cell_name, cell_inst) = self.sram.get_cell_name(self.sram.name, 0, 0) + # storage_names = cell_inst.mod.get_storage_net_names() + # debug.check(len(storage_names) == 2, ("Only inverting/non-inverting storage nodes" + # "supported for characterization. Storage nets={0}").format(storage_names)) + cell_name = "xsram:xbank0:xbitcell_array:xbitcell_array:xbit_r0_c0" + storage_names = ("Q", "Q_bar") q_name = cell_name + OPTS.hier_seperator + str(storage_names[0]) qbar_name = cell_name + OPTS.hier_seperator + str(storage_names[1]) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 7816e00b..68cc08b1 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -82,6 +82,7 @@ class lib: debug.info(1, "Slews: {0}".format(self.slews)) debug.info(1, "Loads: {0}".format(self.loads)) debug.info(1, "self.load_slews : {0}".format(self.load_slews)) + def create_corners(self): """ Create corners for characterization. """ # Get the corners from the options file @@ -801,7 +802,8 @@ class lib: # information of checks # run it only the first time - datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors)) + if OPTS.top_process != "memchar": + datasheet.write("{0},{1},".format(self.sram.drc_errors, self.sram.lvs_errors)) # write area datasheet.write(str(self.sram.width * self.sram.height) + ',') diff --git a/compiler/characterizer/measurements.py b/compiler/characterizer/measurements.py index 83eb5f39..75ceb8a5 100644 --- a/compiler/characterizer/measurements.py +++ b/compiler/characterizer/measurements.py @@ -15,15 +15,16 @@ from .charutils import * class spice_measurement(ABC): """Base class for spice stimulus measurements.""" def __init__(self, measure_name, measure_scale=None, has_port=True): - #Names must be unique for correct spice simulation, but not enforced here. + # Names must be unique for correct spice simulation, but not enforced here. self.name = measure_name self.measure_scale = measure_scale - self.has_port = has_port #Needed for error checking - #Some meta values used externally. variables are added here for consistency accross the objects + self.has_port = has_port # Needed for error checking + # Some meta values used externally. variables are added here for consistency accross the objects self.meta_str = None self.meta_add_delay = False + @abstractmethod - def get_measure_function(self): + def measure_function(self): return None @abstractmethod @@ -31,28 +32,25 @@ class spice_measurement(ABC): return None def write_measure(self, stim_obj, input_tuple): - measure_func = self.get_measure_function() - if measure_func == None: - debug.error("Did not set measure function",1) measure_vals = self.get_measure_values(*input_tuple) - measure_func(stim_obj, *measure_vals) + self.measure_function(stim_obj, *measure_vals) def retrieve_measure(self, port=None): self.port_error_check(port) - if port != None: + if port is not None: value = parse_spice_list("timing", "{0}{1}".format(self.name.lower(), port)) else: value = parse_spice_list("timing", "{0}".format(self.name.lower())) - if type(value)!=float or self.measure_scale == None: + if type(value)!=float or self.measure_scale is None: return value else: - return value*self.measure_scale + return value * self.measure_scale def port_error_check(self, port): - if self.has_port and port == None: - debug.error("Cannot retrieve measurement, port input was expected.",1) - elif not self.has_port and port != None: - debug.error("Unexpected port input received during measure retrieval.",1) + if self.has_port and port is None: + debug.error("Cannot retrieve measurement, port input was expected.", 1) + elif not self.has_port and port is not None: + debug.error("Unexpected port input received during measure retrieval.", 1) class delay_measure(spice_measurement): @@ -71,8 +69,18 @@ class delay_measure(spice_measurement): spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd) - def get_measure_function(self): - return stimuli.gen_meas_delay + def measure_function(self, stim_obj, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td): + """ Creates the .meas statement for the measurement of delay """ + measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n" + stim_obj.mf.write(measure_string.format(meas_name.lower(), + trig_name, + trig_val, + trig_dir, + trig_td, + targ_name, + targ_val, + targ_dir, + targ_td)) def set_meas_constants(self, trig_name, targ_name, trig_dir_str, targ_dir_str, trig_vdd, targ_vdd): """Set the constants for this measurement: signal names, directions, and trigger scales""" @@ -91,7 +99,7 @@ class delay_measure(spice_measurement): trig_val = self.trig_val_of_vdd * vdd_voltage targ_val = self.targ_val_of_vdd * vdd_voltage - if port != None: + if port is not None: # For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) trig_name = self.trig_name_no_port.format(port) @@ -121,7 +129,7 @@ class slew_measure(delay_measure): self.trig_val_of_vdd = 0.9 self.targ_val_of_vdd = 0.1 else: - debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str),1) + debug.error("Unrecognised slew measurement direction={}".format(slew_dir_str), 1) self.trig_name_no_port = signal_name self.targ_name_no_port = signal_name @@ -135,8 +143,18 @@ class power_measure(spice_measurement): spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(power_type) - def get_measure_function(self): - return stimuli.gen_meas_power + def measure_function(self, stim_obj, meas_name, t_initial, t_final): + """ Creates the .meas statement for the measurement of avg power """ + # power mea cmd is different in different spice: + if OPTS.spice_name == "hspice": + power_exp = "power" + else: + # FIXME: Obtain proper vdd and gnd name + power_exp = "par('(-1*v(" + "vdd" + ")*I(v" + "vdd" + "))')" + stim_obj.mf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(), + power_exp, + t_initial, + t_final)) def set_meas_constants(self, power_type): """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" @@ -146,7 +164,7 @@ class power_measure(spice_measurement): def get_measure_values(self, t_initial, t_final, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" self.port_error_check(port) - if port != None: + if port is not None: meas_name = "{}{}".format(self.name, port) else: meas_name = self.name @@ -160,8 +178,15 @@ class voltage_when_measure(spice_measurement): spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(trig_name, targ_name, trig_dir_str, trig_vdd) - def get_measure_function(self): - return stimuli.gen_meas_find_voltage + def measure_function(self, stim_obj, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td): + """ Creates the .meas statement for the measurement of delay """ + measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n" + stim_obj.mf.write(measure_string.format(meas_name.lower(), + targ_name, + trig_name, + trig_val, + trig_dir, + trig_td)) def set_meas_constants(self, trig_name, targ_name, trig_dir_str, trig_vdd): """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" @@ -173,7 +198,7 @@ class voltage_when_measure(spice_measurement): def get_measure_values(self, trig_td, vdd_voltage, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" self.port_error_check(port) - if port != None: + if port is not None: # For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) trig_name = self.trig_name_no_port.format(port) @@ -194,8 +219,12 @@ class voltage_at_measure(spice_measurement): spice_measurement.__init__(self, measure_name, measure_scale, has_port) self.set_meas_constants(targ_name) - def get_measure_function(self): - return stimuli.gen_meas_find_voltage_at_time + def measure_function(self, stim_obj, meas_name, targ_name, time_at): + """ Creates the .meas statement for voltage at time""" + measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n" + stim_obj.mf.write(measure_string.format(meas_name.lower(), + targ_name, + time_at)) def set_meas_constants(self, targ_name): """Sets values useful for power simulations. This value is only meta related to the lib file (rise/fall)""" @@ -204,7 +233,7 @@ class voltage_at_measure(spice_measurement): def get_measure_values(self, time_at, port=None): """Constructs inputs to stimulus measurement function. Variant values are inputs here.""" self.port_error_check(port) - if port != None: + if port is not None: # For dictionary indexing reasons, the name is formatted differently than the signals meas_name = "{}{}".format(self.name, port) targ_name = self.targ_name_no_port.format(port) diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py index ca811ba2..9a26b2a4 100644 --- a/compiler/characterizer/setup_hold.py +++ b/compiler/characterizer/setup_hold.py @@ -42,7 +42,13 @@ class setup_hold(): self.stim_sp = "sh_stim.sp" temp_stim = OPTS.openram_temp + self.stim_sp self.sf = open(temp_stim, "w") - self.stim = stimuli(self.sf, self.corner) + + # creates and opens the measure file for writing + self.meas_sp = "sh_meas.sp" + temp_meas = OPTS.openram_temp + self.meas_sp + self.mf = open(temp_meas, "w") + + self.stim = stimuli(self.sf, self.mf, self.corner) self.write_header(correct_value) @@ -56,6 +62,7 @@ class setup_hold(): correct_value=correct_value) self.write_clock() + self.sf.write(".include {}\n".format(temp_meas)) self.write_measures(mode=mode, correct_value=correct_value) @@ -63,6 +70,7 @@ class setup_hold(): self.stim.write_control(4 * self.period) self.sf.close() + self.mf.close() def write_header(self, correct_value): """ Write the header file with all the models and the power supplies. """ @@ -131,7 +139,7 @@ class setup_hold(): else: dout_rise_or_fall = "FALL" - self.sf.write("\n* Measure statements for pass/fail verification\n") + self.mf.write("\n* Measure statements for pass/fail verification\n") trig_name = "clk" targ_name = "Q" trig_val = targ_val = 0.5 * self.vdd_voltage @@ -168,29 +176,33 @@ class setup_hold(): target_time=feasible_bound, correct_value=correct_value) self.stim.run_sim(self.stim_sp) - ideal_clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) + ideal_clk_to_q = convert_to_float(parse_spice_list("timing", + "clk2q_delay")) # We use a 1/2 speed clock for some reason... setuphold_time = (feasible_bound - 2 * self.period) if mode == "SETUP": # SETUP is clk-din, not din-clk passing_setuphold_time = -1 * setuphold_time else: passing_setuphold_time = setuphold_time - debug.info(2, "*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}".format(mode, - correct_value, - ideal_clk_to_q, - setuphold_time)) + debug.info(2, "*** {0} CHECK: {1} Ideal Clk-to-Q: {2} Setup/Hold: {3}" + .format(mode, + correct_value, + ideal_clk_to_q, + setuphold_time)) if type(ideal_clk_to_q)!=float: debug.error("Initial hold time fails for data value feasible " - "bound {0} Clk-to-Q {1} Setup/Hold {2}".format(feasible_bound, - ideal_clk_to_q, - setuphold_time), + "bound {0} Clk-to-Q {1} Setup/Hold {2}" + .format(feasible_bound, + ideal_clk_to_q, + setuphold_time), 2) - debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} ".format(mode, - setuphold_time, - feasible_bound, - 2 * self.period)) + debug.info(2, "Checked initial {0} time {1}, data at {2}, clock at {3} " + .format(mode, + setuphold_time, + feasible_bound, + 2 * self.period)) while True: target_time = (feasible_bound + infeasible_bound) / 2 @@ -198,11 +210,12 @@ class setup_hold(): target_time=target_time, correct_value=correct_value) - debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}".format(mode, - correct_value, - target_time, - infeasible_bound, - feasible_bound)) + debug.info(2, "{0} value: {1} Target time: {2} Infeasible: {3} Feasible: {4}" + .format(mode, + correct_value, + target_time, + infeasible_bound, + feasible_bound)) self.stim.run_sim(self.stim_sp) clk_to_q = convert_to_float(parse_spice_list("timing", "clk2q_delay")) diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index c49f0e63..770aeb1a 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -235,7 +235,7 @@ class simulation(): self.add_wmask(wmask, port) self.add_spare_wen("1" * self.num_spare_cols, port) - #Add noops to all other ports. + # Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(unselected_port) @@ -267,7 +267,7 @@ class simulation(): self.add_wmask("0" * self.num_wmasks, port) self.add_spare_wen("0" * self.num_spare_cols, port) - #Add noops to all other ports. + # Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(unselected_port) @@ -356,14 +356,14 @@ class simulation(): self.add_noop_one_port(port) - #Add noops to all other ports. + # Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(unselected_port) def append_cycle_comment(self, port, comment): """Add comment to list to be printed in stimulus file""" - #Clean up time before appending. Make spacing dynamic as well. + # Clean up time before appending. Make spacing dynamic as well. time = "{0:.2f} ns:".format(self.t_current) time_spacing = len(time) + 6 self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), @@ -388,7 +388,7 @@ class simulation(): split_word2 = [x + '_' * (n != 0 and n % 4 == 0) for n, x in enumerate(split_word)] # Join the word unreversed back together new_word = ''.join(reversed(split_word2)) - return(new_word) + return (new_word) # Split extra cols if self.num_spare_cols > 0: @@ -414,9 +414,9 @@ class simulation(): comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, addr, port, - int(t_current/self.period), + int(t_current / self.period), t_current, - t_current+self.period) + t_current + self.period) elif op == "partial_write": str = "\tWriting (partial) {0} to address {1} with mask bit {2} (from port {3}) during cycle {4} ({5}ns - {6}ns)" comment = str.format(word, @@ -454,7 +454,7 @@ class simulation(): for i in range(abits): pin_names.append("{0}{1}_{2}".format(addr_name, port, i)) - #Control signals not finalized. + # Control signals not finalized. for port in range(total_ports): pin_names.append("CSB{0}".format(port)) for port in range(total_ports): @@ -493,11 +493,12 @@ class simulation(): # other initializations can only be done during analysis when a bit has been selected # for testing. - self.sram.bank.graph_exclude_precharge() - self.sram.graph_exclude_addr_dff() - self.sram.graph_exclude_data_dff() - self.sram.graph_exclude_ctrl_dffs() - self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() + if OPTS.top_process != 'memchar': + self.sram.bank.graph_exclude_precharge() + self.sram.graph_exclude_addr_dff() + self.sram.graph_exclude_data_dff() + self.sram.graph_exclude_ctrl_dffs() + self.sram.bank.bitcell_array.graph_exclude_replica_col_bits() def set_internal_spice_names(self): """ @@ -515,9 +516,9 @@ class simulation(): self.sen_name = sen_with_port debug.warning("Error occurred while determining SEN name. Can cause faults in simulation.") - column_addr = self.get_column_addr() + # column_addr = self.get_column_addr() bl_name_port, br_name_port = self.get_bl_name(self.graph.all_paths, port) - port_pos = -1 - len(str(column_addr)) - len(str(port)) + # port_pos = -1 - len(str(column_addr)) - len(str(port)) if bl_name_port.endswith(str(port) + "_" + str(self.bitline_column)): # single port SRAM case, bl will not be numbered eg bl_0 self.bl_name = bl_name_port @@ -535,7 +536,7 @@ class simulation(): '{}{}_{}'.format(self.dout_name, port, self.probe_data)) self.sen_name = self.get_sen_name(self.graph.all_paths) - #debug.info(2, "s_en {}".format(self.sen_name)) + # debug.info(2, "s_en {}".format(self.sen_name)) self.bl_name = "bl{0}_{1}".format(port, OPTS.word_size - 1) self.br_name = "br{0}_{1}".format(port, OPTS.word_size - 1) @@ -563,10 +564,10 @@ class simulation(): Creates timing graph to generate the timing paths for the SRAM output. """ - #Make exclusions dependent on the bit being tested. + # Make exclusions dependent on the bit being tested. self.sram.clear_exclude_bits() # Removes previous bit exclusions self.sram.graph_exclude_bits(self.wordline_row, self.bitline_column) - port=self.read_ports[0] #FIXME, port_data requires a port specification, assuming single port for now + port=self.read_ports[0] # FIXME, port_data requires a port specification, assuming single port for now if self.words_per_row > 1: self.sram.graph_clear_column_mux(port) self.sram.graph_exclude_column_mux(self.bitline_column, port) diff --git a/compiler/characterizer/sram_op.py b/compiler/characterizer/sram_op.py deleted file mode 100644 index 3aeca925..00000000 --- a/compiler/characterizer/sram_op.py +++ /dev/null @@ -1,15 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# - -from enum import Enum - -class sram_op(Enum): - READ_ZERO = 0 - READ_ONE = 1 - WRITE_ZERO = 2 - WRITE_ONE = 3 diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py index 514c4c4e..45997b7e 100644 --- a/compiler/characterizer/stimuli.py +++ b/compiler/characterizer/stimuli.py @@ -22,7 +22,7 @@ from openram import OPTS class stimuli(): """ Class for providing stimuli functions """ - def __init__(self, stim_file, corner): + def __init__(self, stim_file, meas_file, corner): self.vdd_name = "vdd" self.gnd_name = "gnd" self.pmos_name = tech.spice["pmos"] @@ -31,6 +31,7 @@ class stimuli(): self.tx_length = tech.drc["minlength_channel"] self.sf = stim_file + self.mf = meas_file (self.process, self.voltage, self.temperature) = corner found = False @@ -136,7 +137,7 @@ class stimuli(): offset, t_rise, t_fall, - 0.5*period-0.5*t_rise-0.5*t_fall, + 0.5 * period - 0.5 * t_rise - 0.5 * t_fall, period)) def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup): @@ -181,7 +182,7 @@ class stimuli(): def gen_meas_delay(self, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, trig_td, targ_td): """ Creates the .meas statement for the measurement of delay """ measure_string=".meas tran {0} TRIG v({1}) VAL={2} {3}=1 TD={4}n TARG v({5}) VAL={6} {7}=1 TD={8}n\n\n" - self.sf.write(measure_string.format(meas_name.lower(), + self.mf.write(measure_string.format(meas_name.lower(), trig_name, trig_val, trig_dir, @@ -194,7 +195,7 @@ class stimuli(): def gen_meas_find_voltage(self, meas_name, trig_name, targ_name, trig_val, trig_dir, trig_td): """ Creates the .meas statement for the measurement of delay """ measure_string=".meas tran {0} FIND v({1}) WHEN v({2})={3}v {4}=1 TD={5}n \n\n" - self.sf.write(measure_string.format(meas_name.lower(), + self.mf.write(measure_string.format(meas_name.lower(), targ_name, trig_name, trig_val, @@ -204,7 +205,7 @@ class stimuli(): def gen_meas_find_voltage_at_time(self, meas_name, targ_name, time_at): """ Creates the .meas statement for voltage at time""" measure_string=".meas tran {0} FIND v({1}) AT={2}n \n\n" - self.sf.write(measure_string.format(meas_name.lower(), + self.mf.write(measure_string.format(meas_name.lower(), targ_name, time_at)) @@ -215,15 +216,15 @@ class stimuli(): power_exp = "power" else: power_exp = "par('(-1*v(" + str(self.vdd_name) + ")*I(v" + str(self.vdd_name) + "))')" - self.sf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(), + self.mf.write(".meas tran {0} avg {1} from={2}n to={3}n\n\n".format(meas_name.lower(), power_exp, t_initial, t_final)) def gen_meas_value(self, meas_name, dout, t_initial, t_final): measure_string=".meas tran {0} FIND v({1}) AT={2}n\n\n".format(meas_name.lower(), dout, (t_initial + t_final) / 2) - #measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final) - self.sf.write(measure_string) + # measure_string=".meas tran {0} AVG v({1}) FROM={2}n TO={3}n\n\n".format(meas_name.lower(), dout, t_initial, t_final) + self.mf.write(measure_string) def write_control(self, end_time, runlvl=4): """ Write the control cards to run and end the simulation """ @@ -278,7 +279,7 @@ class stimuli(): self.sf.write(".OPTIONS DEVICE TEMP={}\n".format(self.temperature)) self.sf.write(".OPTIONS MEASURE MEASFAIL=1\n") self.sf.write(".OPTIONS LINSOL type=klu\n") - self.sf.write(".OPTIONS TIMEINT RELTOL=1e-6 ABSTOL=1e-10 method=gear minorder=2\n") + self.sf.write(".OPTIONS TIMEINT RELTOL=1e-3 ABSTOL=1e-6 method=gear minorder=2\n") # Format: .TRAN self.sf.write(".TRAN {0}p {1}n 0n {0}p\n".format(timestep, end_time)) elif OPTS.spice_name: @@ -411,12 +412,12 @@ class stimuli(): from openram import CONDA_HOME cmd = "source {0}/bin/activate && {1} && conda deactivate".format(CONDA_HOME, cmd) debug.info(2, cmd) - retcode = subprocess.call(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True) + proc = subprocess.run(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True) spice_stdout.close() spice_stderr.close() - if (retcode > valid_retcode): + if (proc.returncode > valid_retcode): debug.error("Spice simulation error: " + cmd, -1) else: end_time = datetime.datetime.now() diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index 73f0da65..ba606cf3 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -85,14 +85,15 @@ class trim_spice(): wl_regex = r"wl\d*_{}".format(wl_address) bl_regex = r"bl\d*_{}".format(int(self.words_per_row*data_bit + col_address)) + bl_no_port_regex = r"bl_{}".format(int(self.words_per_row*data_bit + col_address)) + self.remove_insts("bitcell_array",[wl_regex,bl_regex]) # 2. Keep sense amps basd on BL - # FIXME: The bit lines are not indexed the same in sense_amp_array - #self.remove_insts("sense_amp_array",[bl_regex]) + self.remove_insts("sense_amp_array",[bl_no_port_regex]) # 3. Keep column muxes basd on BL - self.remove_insts("column_mux_array", [bl_regex]) + self.remove_insts("single_level_column_mux_array", [bl_no_port_regex]) # 4. Keep write driver based on DATA data_regex = r"data_{}".format(data_bit) diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 0dbc65b8..969f4fb8 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # See LICENSE for licensing information. # # Copyright (c) 2016-2023 Regents of the University of California and The Board @@ -15,6 +15,7 @@ corner, but should probably be extended. import sys from globals import * +from importlib import reload (OPTS, args) = parse_args() @@ -41,36 +42,29 @@ slew = float(args[3]) import debug init_openram(config_file=config_file, is_unit_test=False) + +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +OPTS.netlist_only = True OPTS.check_lvsdrc = False # Put the temp output in the output path since it is what we want to generate! old_openram_temp = OPTS.openram_temp OPTS.openram_temp = OPTS.output_path - - -import sram -class fake_sram(sram.sram): - """ This is an SRAM that doesn't actually create itself, just computes - the sizes. """ - def __init__(self, word_size, num_words, num_banks, name, num_spare_rows): - self.name = name - self.word_size = word_size - self.num_words = num_words - self.num_banks = num_banks - self.num_spare_rows = num_spare_rows - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - # to get the row, col, etc. - self.compute_sizes() - -sram = fake_sram(OPTS.word_size, OPTS.num_words, OPTS.num_banks, OPTS.output_name) -sp_file = OPTS.output_path+OPTS.output_name + ".sp" +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) from characterizer import delay import tech # Set up the delay and set to the nominal corner -d = delay.delay(sram, sp_file, ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"])) +d = delay(s, s.get_sp_name(), ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"])) # Set the period d.period = period # Set the load of outputs and slew of inputs @@ -91,4 +85,3 @@ print("Output files are:\n{0}stim.sp\n{0}sram.sp\n{0}reduced.sp".format(OPTS.out OPTS.openram_temp = old_openram_temp # Delete temp files, remove the dir, etc. end_openram() - diff --git a/compiler/modules/column_decoder.py b/compiler/modules/column_decoder.py index 92149a71..ab80b1c9 100644 --- a/compiler/modules/column_decoder.py +++ b/compiler/modules/column_decoder.py @@ -42,7 +42,7 @@ class column_decoder(design): def create_instances(self): self.column_decoder_inst = self.add_inst(name="column_decoder", mod=self.column_decoder) - self.connect_inst(self.pins) + self.connect_inst(list(self.pins)) def create_layout(self): self.column_decoder_inst.place(vector(0,0)) diff --git a/compiler/modules/pbitcell.py b/compiler/modules/pbitcell.py index 2eab6e28..9c1800bd 100644 --- a/compiler/modules/pbitcell.py +++ b/compiler/modules/pbitcell.py @@ -1212,7 +1212,7 @@ class pbitcell(bitcell_base): if self.dummy_bitcell: return - pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)} + pin_dict = {pin: port for pin, port in zip(list(self.pins), port_nets)} # Edges added wl->bl, wl->br for every port except write ports rw_pin_names = zip(self.r_wl_names, self.r_bl_names, self.r_br_names) diff --git a/compiler/modules/rom_bank.py b/compiler/modules/rom_bank.py index e4d22f1c..244cbe16 100644 --- a/compiler/modules/rom_bank.py +++ b/compiler/modules/rom_bank.py @@ -14,7 +14,6 @@ from openram.base import rom_verilog from openram import OPTS, print_time from openram.sram_factory import factory from openram.tech import drc, layer, parameter -from openram.router import router_tech class rom_bank(design,rom_verilog): @@ -111,21 +110,16 @@ class rom_bank(design,rom_verilog): self.place_top_level_pins() self.route_output_buffers() - rt = router_tech(self.supply_stack, 1) - init_bbox = self.get_bbox(side="ring", - margin=rt.track_width) - self.route_supplies(init_bbox) - # We need the initial bbox for the supply rings later - # because the perimeter pins will change the bbox + # FIXME: Somehow ROM layout behaves weird and doesn't add all the pin + # shapes before routing supplies + init_bbox = self.get_bbox() + if OPTS.route_supplies: + self.route_supplies(init_bbox) # Route the pins to the perimeter if OPTS.perimeter_pins: # We now route the escape routes far enough out so that they will # reach past the power ring or stripes on the sides - bbox = self.get_bbox(side="ring", - margin=11*rt.track_width) - self.route_escape_pins(bbox) - - + self.route_escape_pins(init_bbox) def setup_layout_constants(self): @@ -450,24 +444,17 @@ class rom_bank(design,rom_verilog): pin_num = msb - self.col_bits self.add_io_pin(self.decode_inst, "A{}".format(pin_num), name) - def route_supplies(self, bbox=None): + def route_supplies(self, bbox): for pin_name in ["vdd", "gnd"]: for inst in self.insts: self.copy_power_pins(inst, pin_name) - if not OPTS.route_supplies: - # Do not route the power supply (leave as must-connect pins) - return - elif OPTS.route_supplies == "grid": - from openram.router import supply_grid_router as router - else: - from openram.router import supply_tree_router as router - rtr=router(layers=self.supply_stack, - design=self, - bbox=bbox, - pin_type=OPTS.supply_pin_type) - + from openram.router import supply_router as router + rtr = router(layers=self.supply_stack, + design=self, + bbox=bbox, + pin_type=OPTS.supply_pin_type) rtr.route() if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]: @@ -507,7 +494,7 @@ class rom_bank(design,rom_verilog): pins_to_route.append("clk") pins_to_route.append("cs") from openram.router import signal_escape_router as router - rtr=router(layers=self.m3_stack, - design=self, - bbox=bbox) - rtr.escape_route(pins_to_route) + rtr = router(layers=self.m3_stack, + bbox=bbox, + design=self) + rtr.route(pins_to_route) diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 89d7a058..48e6cf89 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -14,7 +14,6 @@ from openram.base import channel_route from openram.base import design from openram.base import verilog from openram.base import lef -from openram.router import router_tech from openram.sram_factory import factory from openram.tech import spice from openram import OPTS, print_time @@ -252,18 +251,11 @@ class sram_1bank(design, verilog, lef): for inst in self.insts: self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) - if not OPTS.route_supplies: - # Do not route the power supply (leave as must-connect pins) - return - elif OPTS.route_supplies == "grid": - from openram.router import supply_grid_router as router - else: - from openram.router import supply_tree_router as router - rtr=router(layers=self.supply_stack, - design=self, - bbox=bbox, - pin_type=OPTS.supply_pin_type) - + from openram.router import supply_router as router + rtr = router(layers=self.supply_stack, + design=self, + bbox=bbox, + pin_type=OPTS.supply_pin_type) rtr.route() if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]: @@ -288,7 +280,7 @@ class sram_1bank(design, verilog, lef): pin.width(), pin.height()) - elif OPTS.route_supplies and OPTS.supply_pin_type == "single": + elif OPTS.supply_pin_type == "single": # Update these as we may have routed outside the region (perimeter pins) lowest_coord = self.find_lowest_coords() @@ -326,7 +318,7 @@ class sram_1bank(design, verilog, lef): # Grid is left with many top level pins pass - def route_escape_pins(self, bbox): + def route_escape_pins(self, bbox=None): """ Add the top-level pins for a single bank SRAM with control. """ @@ -370,10 +362,10 @@ class sram_1bank(design, verilog, lef): pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) from openram.router import signal_escape_router as router - rtr=router(layers=self.m3_stack, - design=self, - bbox=bbox) - rtr.escape_route(pins_to_route) + rtr = router(layers=self.m3_stack, + bbox=bbox, + design=self) + rtr.route(pins_to_route) def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ @@ -1077,24 +1069,15 @@ class sram_1bank(design, verilog, lef): # Some technologies have an isolation self.add_dnwell(inflate=2.5) + init_bbox = self.get_bbox() # Route the supplies together and/or to the ring/stripes. - # This is done with the original bbox since the escape routes need to - # be outside of the ring for OpenLane - rt = router_tech(self.supply_stack, 1) - init_bbox = self.get_bbox(side="ring", - margin=rt.track_width) - - # We need the initial bbox for the supply rings later - # because the perimeter pins will change the bbox # Route the pins to the perimeter if OPTS.perimeter_pins: # We now route the escape routes far enough out so that they will # reach past the power ring or stripes on the sides - bbox = self.get_bbox(side="ring", - margin=11*rt.track_width) - self.route_escape_pins(bbox) - - self.route_supplies(init_bbox) + self.route_escape_pins(init_bbox) + if OPTS.route_supplies: + self.route_supplies(init_bbox) def route_dffs(self, add_routes=True): diff --git a/compiler/options.py b/compiler/options.py index 3a9bb3a8..38a38f26 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -104,6 +104,8 @@ class options(optparse.Values): sim_data_path = None # A list of load/slew tuples use_specified_load_slew = None + # Spice simulation raw file + spice_raw_file = None ################### # Run-time vs accuracy options. @@ -113,7 +115,7 @@ class options(optparse.Values): # When enabled, layout is not generated (and no DRC or LVS are performed) netlist_only = False # Whether we should do the final power routing - route_supplies = "tree" + route_supplies = True supply_pin_type = "ring" # This determines whether LVS and DRC is checked at all. check_lvsdrc = False @@ -126,14 +128,18 @@ class options(optparse.Values): # Output config with all options output_extended_config = False # Output temporary file used to format HTML page - output_datasheet_info = False + output_datasheet_info = True # Determines which analytical model to use. # Available Models: elmore, linear_regression model_name = "elmore" + # Write graph to a file + write_graph = False ################### # Tool options ################### + # Top process that was ran (openram, memchar, memfunc) + top_process = None # Use conda to install the default tools # (existing tools will be used if disabled) use_conda = True @@ -174,6 +180,15 @@ class options(optparse.Values): # Purge the temp directory after a successful # run (doesn't purge on errors, anyhow) + # Bitline, s_en and cell names used in characterizer + bl_format = "X{name}{hier_sep}xbank0{hier_sep}bl_{row}_{col}" + br_format = "X{name}{hier_sep}xbank0{hier_sep}br_{row}_{col}" + sen_format = "X{name}{hier_sep}xbank0{hier_sep}s_en" + cell_format = "X{name}{hier_sep}xbank0{hier_sep}xbitcell_array{hier_sep}xreplica_bitcell_array{hier_sep}xbitcell_array{hier_sep}xbit_r{row}_c{col}" + + # Random seed for functional simulation + functional_seed = None + # Route the input/output pins to the perimeter perimeter_pins = True diff --git a/compiler/router/__init__.py b/compiler/router/__init__.py index e428adda..ac2a64ec 100644 --- a/compiler/router/__init__.py +++ b/compiler/router/__init__.py @@ -3,8 +3,5 @@ # Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz # All rights reserved. # -from .router import * from .signal_escape_router import * -from .signal_router import * -from .supply_grid_router import * -from .supply_tree_router import * +from .supply_router import * diff --git a/compiler/router/direction.py b/compiler/router/direction.py deleted file mode 100644 index bcca1e94..00000000 --- a/compiler/router/direction.py +++ /dev/null @@ -1,75 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from enum import Enum -from openram import debug -from openram.base.vector3d import vector3d - - -class direction(Enum): - NORTH = 1 - SOUTH = 2 - EAST = 3 - WEST = 4 - UP = 5 - DOWN = 6 - NORTHEAST = 7 - NORTHWEST = 8 - SOUTHEAST = 9 - SOUTHWEST = 10 - - def get_offset(direct): - """ - Returns the vector offset for a given direction. - """ - if direct==direction.NORTH: - offset = vector3d(0, 1, 0) - elif direct==direction.SOUTH: - offset = vector3d(0, -1 ,0) - elif direct==direction.EAST: - offset = vector3d(1, 0, 0) - elif direct==direction.WEST: - offset = vector3d(-1, 0, 0) - elif direct==direction.UP: - offset = vector3d(0, 0, 1) - elif direct==direction.DOWN: - offset = vector3d(0, 0, -1) - elif direct==direction.NORTHEAST: - offset = vector3d(1, 1, 0) - elif direct==direction.NORTHWEST: - offset = vector3d(-1, 1, 0) - elif direct==direction.SOUTHEAST: - offset = vector3d(1, -1, 0) - elif direct==direction.SOUTHWEST: - offset = vector3d(-1, -1, 0) - else: - debug.error("Invalid direction {}".format(direct)) - - return offset - - def cardinal_directions(up_down_too=False): - temp_dirs = [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST] - if up_down_too: - temp_dirs.extend([direction.UP, direction.DOWN]) - return temp_dirs - - def cardinal_offsets(up_down_too=False): - return [direction.get_offset(d) for d in direction.cardinal_directions(up_down_too)] - - def all_directions(): - return [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST, - direction.NORTHEAST, direction.NORTHWEST, direction.SOUTHEAST, direction.SOUTHWEST] - - def all_offsets(): - return [direction.get_offset(d) for d in direction.all_directions()] - - def all_neighbors(cell): - return [cell + x for x in direction.all_offsets()] - - def cardinal_neighbors(cell): - return [cell + x for x in direction.cardinal_offsets()] - diff --git a/compiler/router/graph.py b/compiler/router/graph.py new file mode 100644 index 00000000..132681c9 --- /dev/null +++ b/compiler/router/graph.py @@ -0,0 +1,442 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +import heapq +from copy import deepcopy +from openram import debug +from openram.base.vector import vector +from openram.base.vector3d import vector3d +from openram.tech import drc +from .graph_node import graph_node +from .graph_probe import graph_probe +from .graph_utils import snap + + +class graph: + """ This is the graph created from the blockages. """ + + def __init__(self, router): + + # This is the graph router that uses this graph + self.router = router + self.source_nodes = [] + self.target_nodes = [] + + + def is_routable(self, shape): + """ Return if a shape is routable in this graph. """ + + return shape.name == self.source.name + + + def inside_shape(self, point, shape): + """ Return if the point is inside the shape. """ + + # Check if they're on the same layer + if point.z != self.router.get_zindex(shape.lpp): + return False + # Check if the point is inside the shape + ll, ur = shape.rect + return shape.on_segment(ll, point, ur) + + + def get_safe_pin_values(self, pin): + """ Get the safe x and y values of the given pin. """ + + # Constant values + pin = pin.get_core() + offset = self.router.half_wire + spacing = self.router.track_space + size_limit = snap(offset * 4 + spacing) + + x_values = [] + y_values = [] + # If one axis size of the pin is greater than the limit, we will take + # two points at both ends. Otherwise, we will only take the center of + # this pin. + if pin.width() > size_limit: + x_values.append(snap(pin.lx() + offset)) + x_values.append(snap(pin.rx() - offset)) + else: + x_values.append(snap(pin.cx())) + if pin.height() > size_limit: + y_values.append(snap(pin.by() + offset)) + y_values.append(snap(pin.uy() - offset)) + else: + y_values.append(snap(pin.cy())) + + return x_values, y_values + + + def is_probe_blocked(self, p1, p2): + """ + Return if a probe sent from p1 to p2 encounters a blockage. + The probe must be sent vertically or horizontally. + This function assumes that p1 and p2 are on the same layer. + """ + + probe_shape = graph_probe(p1, p2, self.router.get_lpp(p1.z)) + pll, pur = probe_shape.rect + # Check if any blockage blocks this probe + for blockage in self.graph_blockages: + bll, bur = blockage.rect + # Not overlapping + if bll.x > pur.x or pll.x > bur.x or bll.y > pur.y or pll.y > bur.y: + continue + # Not on the same layer + if not blockage.same_lpp(blockage.lpp, probe_shape.lpp): + continue + # Probe is blocked if the shape isn't routable + if not self.is_routable(blockage): + return True + blockage = blockage.get_core() + bll, bur = blockage.rect + # Not overlapping + if bll.x > pur.x or pll.x > bur.x or bll.y > pur.y or pll.y > bur.y: + return True + return False + + + def is_node_blocked(self, node, pin_safe=True): + """ Return if a node is blocked by a blockage. """ + + p = node.center + x = p.x + y = p.y + z = p.z + + def closest(value, checklist): + """ Return the distance of the closest value in the checklist. """ + diffs = [abs(value - other) for other in checklist] + return snap(min(diffs)) + + wide = self.router.track_wire + half_wide = self.router.half_wire + spacing = snap(self.router.track_space + half_wide + drc["grid"]) + blocked = False + for blockage in self.graph_blockages: + ll, ur = blockage.rect + # Not overlapping + if ll.x > x or x > ur.x or ll.y > y or y > ur.y: + continue + # Not on the same layer + if self.router.get_zindex(blockage.lpp) != z: + continue + # Blocked if not routable + if not self.is_routable(blockage): + blocked = True + continue + blockage = blockage.get_core() + ll, ur = blockage.rect + # Not overlapping + if ll.x > x or x > ur.x or ll.y > y or y > ur.y: + blocked = True + continue + # Check if the node is too close to one edge of the shape + lengths = [blockage.width(), blockage.height()] + centers = blockage.center() + ll, ur = blockage.rect + safe = [True, True] + for i in range(2): + if lengths[i] >= wide: + min_diff = closest(p[i], [ll[i], ur[i]]) + if min_diff < half_wide: + safe[i] = False + elif centers[i] != p[i]: + safe[i] = False + if not all(safe): + blocked = True + continue + # Check if the node is in a safe region of the shape + xs, ys = self.get_safe_pin_values(blockage) + xdiff = closest(p.x, xs) + ydiff = closest(p.y, ys) + if xdiff == 0 and ydiff == 0: + if pin_safe and blockage in [self.source, self.target]: + return False + elif xdiff < spacing and ydiff < spacing: + blocked = True + return blocked + + + def is_via_blocked(self, nodes): + """ Return if a via on the given point is blocked by another via. """ + + # If the nodes are blocked by a blockage other than a via + for node in nodes: + if self.is_node_blocked(node, pin_safe=False): + return True + # If the nodes are blocked by a via + x = node.center.x + y = node.center.y + z = node.center.z + for via in self.graph_vias: + ll, ur = via.rect + # Not overlapping + if ll.x > x or x > ur.x or ll.y > y or y > ur.y: + continue + center = via.center() + # If not in the center + if center.x != x or center.y != y: + return True + return False + + + def create_graph(self, source, target, scale=1): + """ Create the graph to run routing on later. """ + debug.info(2, "Creating the graph for source '{}' and target'{}'.".format(source, target)) + + # Save source and target information + self.source = source + self.target = target + + # Find the region to be routed and only include objects inside that region + region = deepcopy(source) + region.bbox([target]) + region.multiply(scale) + region = region.inflated_pin(spacing=self.router.track_space) + debug.info(3, "Routing region is {}".format(region.rect)) + + # Find the blockages that are in the routing area + self.graph_blockages = [] + self.find_graph_blockages(region) + + # Find the vias that are in the routing area + self.graph_vias = [] + self.find_graph_vias(region) + + # Generate the cartesian values from shapes in the area + x_values, y_values = self.generate_cartesian_values() + # Adjust the routing region to include "edge" shapes + region.bbox(self.graph_blockages) + # Find and include edge shapes to prevent DRC errors + self.find_graph_blockages(region) + # Generate the graph nodes from cartesian values + self.generate_graph_nodes(x_values, y_values) + # Save the graph nodes that lie in source and target shapes + self.save_end_nodes() + debug.info(3, "Number of blockages detected in the routing region: {}".format(len(self.graph_blockages))) + debug.info(3, "Number of vias detected in the routing region: {}".format(len(self.graph_vias))) + debug.info(3, "Number of nodes in the routing graph: {}".format(len(self.nodes))) + + # Return the region to scale later if no path is found + return region.rect + + + def find_graph_blockages(self, region): + """ Find blockages that overlap the routing region. """ + + for blockage in self.router.blockages: + # Skip if already included + if blockage in self.graph_blockages: + continue + # Set the region's lpp to current blockage's lpp so that the + # overlaps method works + region.lpp = blockage.lpp + if region.overlaps(blockage): + self.graph_blockages.append(blockage) + # Make sure that the source or target fake pins are included as blockage + for shape in [self.source, self.target]: + for blockage in self.graph_blockages: + blockage = blockage.get_core() + if shape == blockage: + break + else: + self.graph_blockages.append(shape) + + + def find_graph_vias(self, region): + """ Find vias that overlap the routing region. """ + + for via in self.router.vias: + # Skip if already included + if via in self.graph_vias: + continue + # Set the regions's lpp to current via's lpp so that the + # overlaps method works + region.lpp = via.lpp + if region.overlaps(via): + self.graph_vias.append(via) + + + def generate_cartesian_values(self): + """ + Generate x and y values from all the corners of the shapes in the + routing region. + """ + + x_values = set() + y_values = set() + + # Add inner values for blockages of the routed type + for shape in self.graph_blockages: + if not self.is_routable(shape): + continue + # Get the safe pin values + xs, ys = self.get_safe_pin_values(shape) + x_values.update(xs) + y_values.update(ys) + + # Add corners for blockages + offset = vector([drc["grid"]] * 2) + for blockage in self.graph_blockages: + ll, ur = blockage.rect + # Add minimum offset to the blockage corner nodes to prevent overlap + nll = snap(ll - offset) + nur = snap(ur + offset) + x_values.update([nll.x, nur.x]) + y_values.update([nll.y, nur.y]) + + # Add center values for existing vias + for via in self.graph_vias: + p = via.center() + x_values.add(p.x) + y_values.add(p.y) + + # Sort x and y values + x_values = list(x_values) + y_values = list(y_values) + x_values.sort() + y_values.sort() + + return x_values, y_values + + + def generate_graph_nodes(self, x_values, y_values): + """ + Generate all graph nodes using the cartesian values and connect the + orthogonal neighbors. + """ + + # Generate all nodes + self.nodes = [] + for x in x_values: + for y in y_values: + for z in [0, 1]: + self.nodes.append(graph_node([x, y, z])) + + # Mark nodes that will be removed + self.mark_blocked_nodes() + + # Connect closest nodes that won't be removed + def search(index, condition, shift): + """ Search and connect neighbor nodes. """ + base_nodes = self.nodes[index:index+2] + found = [base_nodes[0].remove, + base_nodes[1].remove] + while condition(index) and not all(found): + nodes = self.nodes[index - shift:index - shift + 2] + for k in range(2): + if not found[k] and not nodes[k].remove: + found[k] = True + if not self.is_probe_blocked(base_nodes[k].center, nodes[k].center): + base_nodes[k].add_neighbor(nodes[k]) + index -= shift + y_len = len(y_values) + for i in range(0, len(self.nodes), 2): + search(i, lambda count: (count / 2) % y_len, 2) # Down + search(i, lambda count: (count / 2) >= y_len, y_len * 2) # Left + if not self.nodes[i].remove and \ + not self.nodes[i + 1].remove and \ + not self.is_via_blocked(self.nodes[i:i+2]): + self.nodes[i].add_neighbor(self.nodes[i + 1]) + + # Remove marked nodes + self.remove_blocked_nodes() + + + def mark_blocked_nodes(self): + """ Mark graph nodes to be removed that are blocked by a blockage. """ + + for i in range(len(self.nodes) - 1, -1, -1): + node = self.nodes[i] + if self.is_node_blocked(node): + node.remove = True + + + def remove_blocked_nodes(self): + """ Remove graph nodes that are marked to be removed. """ + + for i in range(len(self.nodes) - 1, -1, -1): + node = self.nodes[i] + if node.remove: + node.remove_all_neighbors() + self.nodes.remove(node) + + + def save_end_nodes(self): + """ Save graph nodes that are inside source and target pins. """ + + for node in self.nodes: + if self.inside_shape(node.center, self.source): + self.source_nodes.append(node) + elif self.inside_shape(node.center, self.target): + self.target_nodes.append(node) + + + def find_shortest_path(self): + """ + Find the shortest path from the source node to target node using the + A* algorithm. + """ + + # Heuristic function to calculate the scores + def h(node): + """ Return the estimated distance to the closest target. """ + min_dist = float("inf") + for t in self.target_nodes: + dist = t.center.distance(node.center) + abs(t.center.z - node.center.z) + if dist < min_dist: + min_dist = dist + return min_dist + + # Initialize data structures to be used for A* search + queue = [] + close_set = set() + came_from = {} + g_scores = {} + f_scores = {} + + # Initialize score values for the source nodes + for node in self.source_nodes: + g_scores[node.id] = 0 + f_scores[node.id] = h(node) + heapq.heappush(queue, (f_scores[node.id], node.id, node)) + + # Run the A* algorithm + while len(queue) > 0: + # Get the closest node from the queue + current = heapq.heappop(queue)[2] + + # Skip this node if already discovered + if current in close_set: + continue + close_set.add(current) + + # Check if we've reached the target + if current in self.target_nodes: + path = [] + while current.id in came_from: + path.append(current) + current = came_from[current.id] + path.append(current) + return path + + # Get the previous node to better calculate the next costs + prev_node = None + if current.id in came_from: + prev_node = came_from[current.id] + + # Update neighbor scores + for node in current.neighbors: + tentative_score = current.get_edge_cost(node, prev_node) + g_scores[current.id] + if node.id not in g_scores or tentative_score < g_scores[node.id]: + came_from[node.id] = current + g_scores[node.id] = tentative_score + f_scores[node.id] = tentative_score + h(node) + heapq.heappush(queue, (f_scores[node.id], node.id, node)) + + # Return None if not connected + return None diff --git a/compiler/router/graph_node.py b/compiler/router/graph_node.py new file mode 100644 index 00000000..d849673b --- /dev/null +++ b/compiler/router/graph_node.py @@ -0,0 +1,74 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from openram.base.vector3d import vector3d +from openram.tech import drc + + +class graph_node: + """ This class represents a node on the graph. """ + + # This is used to assign unique ids to nodes + next_id = 0 + + def __init__(self, center): + + self.id = graph_node.next_id + graph_node.next_id += 1 + if isinstance(center, vector3d): + self.center = center + else: + self.center = vector3d(center) + self.neighbors = [] + self.remove = False + + + def add_neighbor(self, other): + """ Connect two nodes. """ + + if other not in self.neighbors: + self.neighbors.append(other) + other.neighbors.append(self) + + + def remove_neighbor(self, other): + """ Disconnect two nodes. """ + + if other in self.neighbors: + self.neighbors.remove(other) + other.neighbors.remove(self) + + + def remove_all_neighbors(self): + """ Disconnect all current neighbors. """ + + for neighbor in self.neighbors: + self.neighbors.remove(neighbor) + neighbor.neighbors.remove(self) + + + def get_direction(self, b): + """ Return the direction of path from a to b. """ + + horiz = self.center.x == b.center.x + vert = self.center.y == b.center.y + return (horiz, vert) + + + def get_edge_cost(self, other, prev_node=None): + """ Get the cost of going from this node to the other node. """ + + if other in self.neighbors: + is_vertical = self.center.x == other.center.x + layer_dist = self.center.distance(other.center) + # Double the cost if the edge is in non-preferred direction + if is_vertical != bool(self.center.z): + layer_dist *= 2 + # Add a constant wire cost to prevent dog-legs + if prev_node and self.get_direction(prev_node) != self.get_direction(other): + layer_dist += drc["grid"] + via_dist = abs(self.center.z - other.center.z) + return layer_dist + via_dist + return float("inf") diff --git a/compiler/router/graph_probe.py b/compiler/router/graph_probe.py new file mode 100644 index 00000000..31f06a57 --- /dev/null +++ b/compiler/router/graph_probe.py @@ -0,0 +1,16 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# + +class graph_probe: + """ + This class represents a probe sent from one point to another on Hanan graph. + This is used to mimic the pin_layout class to utilize its methods. + """ + + def __init__(self, p1, p2, lpp): + + self.rect = (p1.min(p2), p1.max(p2)) + self.lpp = lpp diff --git a/compiler/router/graph_shape.py b/compiler/router/graph_shape.py new file mode 100644 index 00000000..1221dabe --- /dev/null +++ b/compiler/router/graph_shape.py @@ -0,0 +1,112 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from openram.base.pin_layout import pin_layout +from openram.base.vector import vector +from openram.tech import drc +from .graph_utils import snap + + +class graph_shape(pin_layout): + """ + This class inherits the pin_layout class to change some of its behavior for + the graph router. + """ + + def __init__(self, name, rect, layer_name_pp, core=None): + + pin_layout.__init__(self, name, rect, layer_name_pp) + + # Snap the shape to the grid here + ll, ur = self.rect + self.rect = [snap(ll), snap(ur)] + # Core is the original shape from which this shape is inflated + self.core = core + + + def center(self): + """ Override the default `center` behavior. """ + + return snap(super().center()) + + + def height(self): + """ Override the default `height` behavior. """ + + return snap(super().height()) + + + def width(self): + """ Override the default `width` behavior. """ + + return snap(super().width()) + + + def rename(self, new_name): + """ Change the name of `self` and `self.core`. """ + + self.name = new_name + self.get_core().name = new_name + + + def get_core(self): + """ + Return `self` if `self.core` is None. Otherwise, return `self.core`. + """ + + if self.core is None: + return self + return self.core + + + def inflated_pin(self, spacing=None, multiple=0.5, extra_spacing=0): + """ Override the default inflated_pin behavior. """ + + ll, ur = self.inflate(spacing, multiple) + extra = vector([extra_spacing] * 2) + newll = ll - extra + newur = ur + extra + inflated_area = (newll, newur) + return graph_shape(self.name, inflated_area, self.layer, self) + + + def multiply(self, scale): + """ Multiply the width and height with the scale value. """ + + width = (self.width() * (scale - 1)) / 2 + height = (self.height() * (scale - 1)) / 2 + ll, ur = self.rect + newll = vector(ll.x - width, ll.y - height) + newur = vector(ur.x + width, ur.y + height) + self.rect = [snap(newll), snap(newur)] + + + def core_contained_by_any(self, shape_list): + """ + Return if the core of this shape is contained by any shape's core in the + given list. + """ + + self_core = self.get_core() + for shape in shape_list: + shape_core = shape.get_core() + if shape_core.contains(self_core): + return True + return False + + + def aligns(self, other): + """ Return if the other shape aligns with this shape. """ + + # Shapes must overlap to be able to align + if not self.overlaps(other): + return False + ll, ur = self.rect + oll, our = other.rect + if ll.x == oll.x and ur.x == our.x: + return True + if ll.y == oll.y and ur.y == our.y: + return True + return False diff --git a/compiler/router/graph_utils.py b/compiler/router/graph_utils.py new file mode 100644 index 00000000..4fa9a301 --- /dev/null +++ b/compiler/router/graph_utils.py @@ -0,0 +1,18 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +""" +Utility functions for graph router. +""" +from openram.base import vector +from openram.tech import drc + + +def snap(a): + """ Use custom `snap` since `vector.snap_to_grid` isn't working. """ + + if isinstance(a, vector): + return vector(snap(a.x), snap(a.y)) + return round(a, len(str(drc["grid"]).split('.')[1])) diff --git a/compiler/router/grid.py b/compiler/router/grid.py deleted file mode 100644 index 0c97ba6c..00000000 --- a/compiler/router/grid.py +++ /dev/null @@ -1,215 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from openram import debug -from openram.base.vector3d import vector3d -from .grid_cell import grid_cell - - -class grid: - """ - A two layer routing map. Each cell can be blocked in the vertical - or horizontal layer. - """ - # costs are relative to a unit grid - # non-preferred cost allows an off-direction jog of 1 grid - # rather than 2 vias + preferred direction (cost 5) - VIA_COST = 2 - NONPREFERRED_COST = 4 - PREFERRED_COST = 1 - - def __init__(self, ll, ur, track_width): - """ Initialize the map and define the costs. """ - - # list of the source/target grid coordinates - self.source = set() - self.target = set() - - self.track_width = track_width - self.track_widths = [self.track_width, self.track_width, 1.0] - self.track_factor = [1 / self.track_width, 1 / self.track_width, 1.0] - - # The bounds are in grids for this - # This is really lower left bottom layer and upper right top layer in 3D. - self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round() - self.ur = vector3d(ur.x, ur.y, 0).scale(self.track_factor).round() - debug.info(1, "BBOX coords: ll=" + str(ll) + " ur=" + str(ur)) - debug.info(1, "BBOX grids: ll=" + str(self.ll) + " ur=" + str(self.ur)) - - # let's leave the map sparse, cells are created on demand to reduce memory - self.map={} - - def add_all_grids(self): - for x in range(self.ll.x, self.ur.x, 1): - for y in range(self.ll.y, self.ur.y, 1): - self.add_map(vector3d(x, y, 0)) - self.add_map(vector3d(x, y, 1)) - - def set_blocked(self, n, value=True): - if not isinstance(n, vector3d): - for item in n: - self.set_blocked(item, value) - else: - self.add_map(n) - self.map[n].blocked=value - - def is_blocked(self, n): - if not isinstance(n, vector3d): - for item in n: - if self.is_blocked(item): - return True - else: - return False - else: - self.add_map(n) - return self.map[n].blocked - - def is_inside(self, n): - if not isinstance(n, vector3d): - for item in n: - if self.is_inside(item): - return True - else: - return False - else: - return n.x >= self.ll.x and n.x <= self.ur.x and n.y >= self.ll.y and n.y <= self.ur.y - - def set_path(self, n, value=True): - if isinstance(n, (list, tuple, set, frozenset)): - for item in n: - self.set_path(item, value) - else: - self.add_map(n) - self.map[n].path=value - - def clear_blockages(self): - for k in self.map: - self.map[k].blocked=False - - def clear_source(self): - for k in self.map: - self.map[k].source=False - self.source = set() - - def set_source(self, n): - if not isinstance(n, vector3d): - for item in n: - self.set_source(item) - else: - self.add_map(n) - self.map[n].source=True - self.map[n].blocked=False - self.source.add(n) - - def clear_target(self): - for k in self.map: - self.map[k].target=False - self.target = set() - - def set_target(self, n): - if not isinstance(n, vector3d): - for item in n: - self.set_target(item) - else: - self.add_map(n) - self.map[n].target=True - self.map[n].blocked=False - self.target.add(n) - - def add_source(self, track_list): - debug.info(3, "Adding source list={0}".format(str(track_list))) - for n in track_list: - debug.info(4, "Adding source ={0}".format(str(n))) - self.set_source(n) - # self.set_blocked(n, False) - - def add_target(self, track_list): - debug.info(3, "Adding target list={0}".format(str(track_list))) - for n in track_list: - debug.info(4, "Adding target ={0}".format(str(n))) - self.set_target(n) - # self.set_blocked(n, False) - - def get_perimeter_list(self, side="left", layers=[0, 1], width=1, margin=0, offset=0): - """ - Side specifies which side. - Layer specifies horizontal (0) or vertical (1) - Width specifies how wide the perimeter "stripe" should be. - Works from the inside out from the bbox (ll, ur) - """ - if "ring" in side: - ring_width = width - else: - ring_width = 0 - - if "ring" in side: - ring_offset = offset - else: - ring_offset = 0 - - perimeter_list = [] - # Add the left/right columns - if side=="all" or "left" in side: - for x in range(self.ll.x - offset, self.ll.x - width - offset, -1): - for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1): - for layer in layers: - perimeter_list.append(vector3d(x, y, layer)) - - if side=="all" or "right" in side: - for x in range(self.ur.x + offset, self.ur.x + width + offset, 1): - for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1): - for layer in layers: - perimeter_list.append(vector3d(x, y, layer)) - - if side=="all" or "bottom" in side: - for y in range(self.ll.y - offset, self.ll.y - width - offset, -1): - for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1): - for layer in layers: - perimeter_list.append(vector3d(x, y, layer)) - - if side=="all" or "top" in side: - for y in range(self.ur.y + offset, self.ur.y + width + offset, 1): - for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1): - for layer in layers: - perimeter_list.append(vector3d(x, y, layer)) - - # Add them all to the map - self.add_map(perimeter_list) - - return perimeter_list - - def add_perimeter_target(self, side="all", layers=[0, 1]): - debug.info(3, "Adding perimeter target") - - perimeter_list = self.get_perimeter_list(side, layers) - - self.set_target(perimeter_list) - - def is_target(self, point): - """ - Point is in the target set, so we are done. - """ - return point in self.target - - def add_map(self, n): - """ - Add a point to the map if it doesn't exist. - """ - if not isinstance(n, vector3d): - for item in n: - self.add_map(item) - else: - if n not in self.map: - self.map[n]=grid_cell() - - def block_path(self, path): - """ - Mark the path in the routing grid as blocked. - Also unsets the path flag. - """ - path.set_path(False) - path.set_blocked(True) diff --git a/compiler/router/grid_cell.py b/compiler/router/grid_cell.py deleted file mode 100644 index 1ef7777a..00000000 --- a/compiler/router/grid_cell.py +++ /dev/null @@ -1,52 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# - -class grid_cell: - """ - A single cell that can be occupied in a given layer, blocked, - visited, etc. - """ - def __init__(self): - self.path = False - self.blocked = False - self.source = False - self.target = False - # -1 means it isn't visited yet - self.min_cost = -1 - - def reset(self): - """ - Reset the dynamic info about routing. - """ - self.min_cost=-1 - self.min_path=None - self.blocked=False - self.source=False - self.target=False - - def get_cost(self): - # We can display the cost of the frontier - if self.min_cost > 0: - return self.min_cost - - def get_type(self): - type_string = "" - - if self.blocked: - type_string += "X" - - if self.source: - type_string += "S" - - if self.target: - type_string += "T" - - if self.path: - type_string += "P" - - return type_string diff --git a/compiler/router/grid_path.py b/compiler/router/grid_path.py deleted file mode 100644 index b7ea5ffc..00000000 --- a/compiler/router/grid_path.py +++ /dev/null @@ -1,215 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from itertools import tee -from openram.base.vector3d import vector3d -from .grid import grid -from .direction import direction - - -class grid_path: - """ - A grid path is a list of lists of grid cells. - It can have a width that is more than one cell. - All of the sublists will be the same dimension. - Cells should be continguous. - It can have a name to define pin shapes as well. - """ - - def __init__(self, items=[], name=""): - self.name = name - if items: - self.pathlist = [items] - else: - self.pathlist = [] - - def __str__(self): - p = str(self.pathlist) - if self.name != "": - return (str(self.name) + " : " + p) - return p - - def __setitem__(self, index, value): - """ - override setitem function - can set value by pathinstance[index]=value - """ - self.pathlist[index]=value - - def __getitem__(self, index): - """ - override getitem function - can get value by value=pathinstance[index] - """ - return self.pathlist[index] - - def __contains__(self, key): - """ - Determine if cell exists in this path - """ - # FIXME: Could maintain a hash to make in O(1) - for sublist in self.pathlist: - for item in sublist: - if item == key: - return True - else: - return False - - def __add__(self, items): - """ - Override add to do append - """ - return self.pathlist.extend(items) - - def __len__(self): - return len(self.pathlist) - - def trim_last(self): - """ - Drop the last item - """ - if len(self.pathlist)>0: - self.pathlist.pop() - - def trim_first(self): - """ - Drop the first item - """ - if len(self.pathlist)>0: - self.pathlist.pop(0) - - def append(self,item): - """ - Append the list of items to the cells - """ - self.pathlist.append(item) - - def extend(self,item): - """ - Extend the list of items to the cells - """ - self.pathlist.extend(item) - - def set_path(self,value=True): - for sublist in self.pathlist: - for p in sublist: - p.path=value - - def set_blocked(self,value=True): - for sublist in self.pathlist: - for p in sublist: - p.blocked=value - - def get_grids(self): - """ - Return a set of all the grids in this path. - """ - newset = set() - for sublist in self.pathlist: - newset.update(sublist) - return newset - - def get_wire_grids(self, start_index, end_index): - """ - Return a set of all the wire grids in this path. - These are the indices in the wave path in a certain range. - """ - newset = set() - for sublist in self.pathlist: - newset.update(sublist[start_index:end_index]) - return newset - - def cost(self): - """ - The cost of the path is the length plus a penalty for the number - of vias. We assume that non-preferred direction is penalized. - This cost only works with 1 wide tracks. - """ - - # Ignore the source pin layer change, FIXME? - def pairwise(iterable): - "s -> (s0,s1), (s1,s2), (s2, s3), ..." - a, b = tee(iterable) - next(b, None) - return zip(a, b) - - plist = list(pairwise(self.pathlist)) - cost = 0 - for p0list,p1list in plist: - # This is because they are "waves" so pick the first item - p0=p0list[0] - p1=p1list[0] - - if p0.z != p1.z: # via - cost += grid.VIA_COST - elif p0.x != p1.x and p0.z==1: # horizontal on vertical layer - cost += grid.NONPREFERRED_COST - elif p0.y != p1.y and p0.z==0: # vertical on horizontal layer - cost += grid.NONPREFERRED_COST - else: - cost += grid.PREFERRED_COST - - return cost - - def expand_dirs(self): - """ - Expand from the end in each of the four cardinal directions plus up - or down but not expanding to blocked cells. Expands in all - directions regardless of preferred directions. - - If the width is more than one, it can only expand in one direction - (for now). This is assumed for the supply router for now. - - """ - neighbors = [] - - for d in direction.cardinal_directions(True): - n = self.neighbor(d) - if n: - neighbors.append(n) - - return neighbors - - def neighbor(self, d): - offset = direction.get_offset(d) - - newwave = [point + offset for point in self.pathlist[-1]] - - if newwave in self.pathlist: - return None - elif newwave[0].z>1 or newwave[0].z<0: - return None - - return newwave - - def set_layer(self, zindex): - new_pathlist = [vector3d(item.x, item.y, zindex) for wave in self.pathlist for item in wave] - self.pathlist = new_pathlist - - def overlap(self, other): - """ - Return the overlap waves ignoring different layers - """ - - my_zindex = self.pathlist[0][0].z - other_flat_cells = [vector3d(item.x,item.y,my_zindex) for wave in other.pathlist for item in wave] - # This keeps the wave structure of the self layer - shared_waves = [] - for wave in self.pathlist: - for item in wave: - # If any item in the wave is not contained, skip it - if not item in other_flat_cells: - break - else: - shared_waves.append(wave) - - if len(shared_waves)>0: - ll = shared_waves[0][0] - ur = shared_waves[-1][-1] - return [ll,ur] - return None - diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py deleted file mode 100644 index 97aaf1cd..00000000 --- a/compiler/router/grid_utils.py +++ /dev/null @@ -1,167 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -""" -Some utility functions for sets of grid cells. -""" - -import math -from openram.base.vector3d import vector3d -from .direction import direction - - -def increment_set(curset, direct): - """ - Return the cells incremented in given direction - """ - offset = direction.get_offset(direct) - - newset = set() - for c in curset: - newc = c+offset - newset.add(newc) - - return newset - - -def remove_border(curset, direct): - """ - Remove the cells on a given border. - """ - border = get_border(curset, direct) - curset.difference_update(border) - - -def get_upper_right(curset): - ur = None - for p in curset: - if ur == None or (p.x>=ur.x and p.y>=ur.y): - ur = p - return ur - - -def get_lower_left(curset): - ll = None - for p in curset: - if ll == None or (p.x<=ll.x and p.y<=ll.y): - ll = p - return ll - - -def get_border(curset, direct): - """ - Return the furthest cell(s) in a given direction. - """ - - # find direction-most cell(s) - maxc = [] - if direct==direction.NORTH: - for c in curset: - if len(maxc)==0 or c.y>maxc[0].y: - maxc = [c] - elif c.y==maxc[0].y: - maxc.append(c) - elif direct==direct.SOUTH: - for c in curset: - if len(maxc)==0 or c.ymaxc[0].x: - maxc = [c] - elif c.x==maxc[0].x: - maxc.append(c) - elif direct==direct.WEST: - for c in curset: - if len(maxc)==0 or c.x 0, - "Did not find any enclosures.") - - return new_pin_list - - def compute_connector(self, pin, enclosure): - """ - Compute a shape to connect the pin to the enclosure shape. - This assumes the shape will be the dimension of the pin. - """ - if pin.xoverlaps(enclosure): - # Is it vertical overlap, extend pin shape to enclosure - plc = pin.lc() - prc = pin.rc() - elc = enclosure.lc() - # erc = enclosure.rc() - ymin = min(plc.y, elc.y) - ymax = max(plc.y, elc.y) - ll = vector(plc.x, ymin) - ur = vector(prc.x, ymax) - elif pin.yoverlaps(enclosure): - # Is it horizontal overlap, extend pin shape to enclosure - pbc = pin.bc() - puc = pin.uc() - ebc = enclosure.bc() - # euc = enclosure.uc() - xmin = min(pbc.x, ebc.x) - xmax = max(pbc.x, ebc.x) - ll = vector(xmin, pbc.y) - ur = vector(xmax, puc.y) - else: - # Neither, so we must do a corner-to corner - pc = pin.center() - ec = enclosure.center() - xmin = min(pc.x, ec.x) - xmax = max(pc.x, ec.x) - ymin = min(pc.y, ec.y) - ymax = max(pc.y, ec.y) - ll = vector(xmin, ymin) - ur = vector(xmax, ymax) - - if ll.x == ur.x or ll.y == ur.y: - return None - p = pin_layout(pin.name, [ll, ur], pin.layer) - return p - - def find_above_connector(self, pin, enclosures): - """ - Find the enclosure that is to above the pin - and make a connector to it's upper edge. - """ - # Create the list of shapes that contain the pin edge - edge_list = [] - for shape in enclosures: - if shape.xcontains(pin): - edge_list.append(shape) - - # Sort them by their bottom edge - edge_list.sort(key=lambda x: x.by(), reverse=True) - - # Find the bottom edge that is next to the pin's top edge - above_item = None - for item in edge_list: - if item.by() >= pin.uy(): - above_item = item - else: - break - - # There was nothing - if not above_item: - return None - # If it already overlaps, no connector needed - if above_item.overlaps(pin): - return None - - # Otherwise, make a connector to the item - p = self.compute_connector(pin, above_item) - return p - - def find_below_connector(self, pin, enclosures): - """ - Find the enclosure that is below the pin - and make a connector to it's upper edge. - """ - # Create the list of shapes that contain the pin edge - edge_list = [] - for shape in enclosures: - if shape.xcontains(pin): - edge_list.append(shape) - - # Sort them by their upper edge - edge_list.sort(key=lambda x: x.uy()) - - # Find the upper edge that is next to the pin's bottom edge - bottom_item = None - for item in edge_list: - if item.uy() <= pin.by(): - bottom_item = item - else: - break - - # There was nothing to the left - if not bottom_item: - return None - # If it already overlaps, no connector needed - if bottom_item.overlaps(pin): - return None - - # Otherwise, make a connector to the item - p = self.compute_connector(pin, bottom_item) - return p - - def find_left_connector(self, pin, enclosures): - """ - Find the enclosure that is to the left of the pin - and make a connector to it's right edge. - """ - # Create the list of shapes that contain the pin edge - edge_list = [] - for shape in enclosures: - if shape.ycontains(pin): - edge_list.append(shape) - - # Sort them by their right edge - edge_list.sort(key=lambda x: x.rx()) - - # Find the right edge that is to the pin's left edge - left_item = None - for item in edge_list: - if item.rx() <= pin.lx(): - left_item = item - else: - break - - # There was nothing to the left - if not left_item: - return None - # If it already overlaps, no connector needed - if left_item.overlaps(pin): - return None - - # Otherwise, make a connector to the item - p = self.compute_connector(pin, left_item) - return p - - def find_right_connector(self, pin, enclosures): - """ - Find the enclosure that is to the right of the pin - and make a connector to it's left edge. - """ - # Create the list of shapes that contain the pin edge - edge_list = [] - for shape in enclosures: - if shape.ycontains(pin): - edge_list.append(shape) - - # Sort them by their right edge - edge_list.sort(key=lambda x: x.lx(), reverse=True) - - # Find the left edge that is next to the pin's right edge - right_item = None - for item in edge_list: - if item.lx() >= pin.rx(): - right_item = item - else: - break - - # There was nothing to the right - if not right_item: - return None - # If it already overlaps, no connector needed - if right_item.overlaps(pin): - return None - - # Otherwise, make a connector to the item - p = self.compute_connector(pin, right_item) - return p - - def find_smallest_connector(self, pin_list, shape_list): - """ - Compute all of the connectors between the overlapping - pins and enclosure shape list. - Return the smallest. - """ - smallest = None - for pin in pin_list: - for enclosure in shape_list: - new_enclosure = self.compute_connector(pin, enclosure) - if not smallest or new_enclosure.area() < smallest.area(): - smallest = new_enclosure - - return smallest - - def find_smallest_overlapping(self, pin_list, shape_list): - """ - Find the smallest area shape in shape_list that overlaps with any - pin in pin_list by a min width. - """ - - smallest_shape = None - for pin in pin_list: - overlap_shape = self.find_smallest_overlapping_pin(pin, shape_list) - if overlap_shape: - # overlap_length = pin.overlap_length(overlap_shape) - if not smallest_shape or overlap_shape.area() < smallest_shape.area(): - smallest_shape = overlap_shape - - return smallest_shape - - def find_smallest_overlapping_pin(self, pin, shape_list): - """ - Find the smallest area shape in shape_list that overlaps with any - pin in pin_list by a min width. - """ - - smallest_shape = None - zindex = self.router.get_zindex(pin.lpp[0]) - (min_width, min_space) = self.router.get_layer_width_space(zindex) - - # Now compare it with every other shape to check how much they overlap - for other in shape_list: - overlap_length = pin.overlap_length(other) - if overlap_length > min_width: - if not smallest_shape or other.area() < smallest_shape.area(): - smallest_shape = other - - return smallest_shape - - def overlap_any_shape(self, pin_list, shape_list): - """ - Does the given pin overlap any of the shapes in the pin list. - """ - for pin in pin_list: - for other in shape_list: - if pin.overlaps(other): - return True - - return False - - def max_pin_layout(self, pin_list): - """ - Return the max area pin_layout - """ - biggest = pin_list[0] - for pin in pin_list: - if pin.area() > biggest.area(): - biggest = pin - - return pin - - def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): - """ - This encloses a single pin component with a rectangle - starting with the seed and expanding dir1 until blocked - and then dir2 until blocked. - dir1 and dir2 should be two orthogonal directions. - """ - - offset1 = direction.get_offset(dir1) - offset2 = direction.get_offset(dir2) - - # We may have started with an empty set - debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.") - - common_blockages = self.router.get_blocked_grids() & self.grids - - # Start with the ll and make the widest row - row = [ll] - # Move in dir1 while we can - while True: - next_cell = row[-1] + offset1 - # Can't move if not in the pin shape - if next_cell in self.grids and next_cell not in common_blockages: - row.append(next_cell) - else: - break - # Move in dir2 while we can - while True: - next_row = [x + offset2 for x in row] - for cell in next_row: - # Can't move if any cell is not in the pin shape - if cell not in self.grids or cell in common_blockages: - break - else: - row = next_row - # Skips the second break - continue - # Breaks from the nested break - break - - # Add a shape from ll to ur - ur = row[-1] - return (ll, ur) - - def enclose_pin(self): - """ - If there is one set of connected pin shapes, - this will find the smallest rectangle enclosure that - overlaps with any pin. - If there is not, it simply returns all the enclosures. - """ - self.enclosed = True - - # Compute the enclosure pin_layout list of the set of tracks - self.enclosures = self.compute_enclosures() - - # Find a connector to every pin and add it to the enclosures - for pin in self.pins: - - # If it is contained, it won't need a connector - if pin.contained_by_any(self.enclosures): - continue - - # Find a connector in the cardinal directions - # If there is overlap, but it isn't contained, - # these could all be None - # These could also be none if the pin is - # diagonal from the enclosure - left_connector = self.find_left_connector(pin, self.enclosures) - right_connector = self.find_right_connector(pin, self.enclosures) - above_connector = self.find_above_connector(pin, self.enclosures) - below_connector = self.find_below_connector(pin, self.enclosures) - connector_list = [left_connector, - right_connector, - above_connector, - below_connector] - filtered_list = list(filter(lambda x: x != None, connector_list)) - if (len(filtered_list) > 0): - import copy - bbox_connector = copy.copy(pin) - bbox_connector.bbox(filtered_list) - self.enclosures.append(bbox_connector) - - # Now, make sure each pin touches an enclosure. - # If not, add another (diagonal) connector. - # This could only happen when there was no enclosure - # in any cardinal direction from a pin - if not self.overlap_any_shape(self.pins, self.enclosures): - connector = self.find_smallest_connector(self.pins, - self.enclosures) - if not connector: - debug.error("Could not find a connector for {} with {}".format(self.pins, - self.enclosures)) - self.router.write_debug_gds("no_connector.gds") - self.enclosures.append(connector) - - # At this point, the pins are overlapping, - # but there might be more than one! - overlap_set = set() - for pin in self.pins: - overlap_set.update(self.transitive_overlap(pin, self.enclosures)) - # Use the new enclosures and recompute the grids - # that correspond to them - if len(overlap_set) < len(self.enclosures): - self.enclosures = overlap_set - self.grids = set() - # Also update the grid locations with the new - # (possibly pruned) enclosures - for enclosure in self.enclosures: - (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name, - enclosure) - self.grids.update(sufficient) - - debug.info(3, "Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name, - self.pins, - self.grids, - self.enclosures)) - - def transitive_overlap(self, shape, shape_list): - """ - Given shape, find the elements in shape_list that overlap transitively. - I.e. if shape overlaps A and A overlaps B, return both A and B. - """ - - augmented_shape_list = set(shape_list) - old_connected_set = set() - connected_set = set([shape]) - # Repeat as long as we expand the set - while len(connected_set) > len(old_connected_set): - old_connected_set = connected_set - connected_set = set([shape]) - for old_shape in old_connected_set: - for cur_shape in augmented_shape_list: - if old_shape.overlaps(cur_shape): - connected_set.add(cur_shape) - - # Remove the original shape - connected_set.remove(shape) - - # if len(connected_set) 87.9 and lx < 87.99 and ly > 18.56 and ly < 18.6: - # breakpoint() - for pin in self.pins: - debug.info(4, " Converting {0}".format(pin)) - # Determine which tracks the pin overlaps - (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name, - pin) - pin_set.update(sufficient) - partial_set.update(insufficient) - - # Blockages will be a super-set of pins since - # it uses the inflated pin shape. - blockage_in_tracks = self.router.convert_blockage(pin) - # Must include the pins here too because these are computed in a different - # way than blockages. - blockages = sufficient | insufficient | blockage_in_tracks - self.blockages.update(blockages) - - # If we have a blockage, we must remove the grids - # Remember, this excludes the pin blockages already - blocked_grids = self.router.get_blocked_grids() - pin_set.difference_update(blocked_grids) - partial_set.difference_update(blocked_grids) - - # At least one of the groups must have some valid tracks - if (len(pin_set) == 0 and len(partial_set) == 0): - # debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) - - for pin in self.pins: - debug.warning(" Expanding conversion {0}".format(pin)) - # Determine which tracks the pin overlaps - (sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name, - pin, - expansion=1) - - # This time, don't remove blockages in the hopes that it might be ok. - # Could cause DRC problems! - pin_set.update(sufficient) - partial_set.update(insufficient) - - # If it's still empty, we must bail. - if len(pin_set) == 0 and len(partial_set) == 0: - debug.error("Unable to find unblocked pin {} {}".format(self.name, - self.pins)) - self.router.write_debug_gds("blocked_pin.gds") - - # Consider the fully connected set first and if not the partial set - # if len(pin_set) > 0: - # self.grids = pin_set - # else: - # self.grids = partial_set - # Just using the full set simplifies the enclosures, otherwise - # we get some pin enclose DRC errors due to off grid pins - self.grids = pin_set | partial_set - if len(self.grids) < 0: - debug.error("Did not find any unblocked grids: {}".format(str(self.pins))) - self.router.write_debug_gds("blocked_pin.gds") - - # Remember the secondary grids for removing adjacent pins - self.secondary_grids = partial_set - - debug.info(4, " pins {}".format(self.grids)) - debug.info(4, " secondary {}".format(self.secondary_grids)) diff --git a/compiler/router/router.py b/compiler/router/router.py index 0ec9665b..fe445ae1 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -1,1531 +1,342 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz # All rights reserved. # -import math -import itertools -from datetime import datetime from openram import debug from openram.base.vector import vector -from openram.base.vector3d import vector3d -from openram.base.pin_layout import pin_layout from openram.gdsMill import gdsMill -from openram.tech import drc, GDS -from openram.tech import layer as techlayer -from openram import OPTS, print_time +from openram.tech import GDS +from openram.tech import drc +from openram.tech import layer as tech_layer +from openram import OPTS +from .graph_shape import graph_shape +from .graph_utils import snap from .router_tech import router_tech -from .pin_group import pin_group -from . import grid_utils class router(router_tech): """ - A router class to read an obstruction map from a gds and plan a - route on a given layer. This is limited to two layer routes. - It populates blockages on a grid class. + This is the base class for routers that use the Hanan grid graph method. """ - def __init__(self, layers, design, bbox=None, margin=0, route_track_width=1): - """ - This will instantiate a copy of the gds file or the module at (0,0) and - route on top of this. The blockages from the gds/module will be - considered. - """ - router_tech.__init__(self, layers, route_track_width) + def __init__(self, layers, design, bbox=None): - self.cell = design + # `router_tech` contains tech constants for the router + router_tech.__init__(self, layers, route_track_width=1) + # Layers that can be used for routing + self.layers = layers + # This is the `hierarchy_layout` object + self.design = design + # Temporary GDSII file name to find pins and blockages self.gds_filename = OPTS.openram_temp + "temp.gds" - - # The pin data structures - # A map of pin names to a set of pin_layout structures - # (i.e. pins with a given label) + # Calculate the bounding box for routing around the perimeter + # FIXME: We wouldn't do this if `rom_bank` wasn't behaving weird + if bbox is None: + self.bbox = self.design.get_bbox(margin=11 * self.track_width) + else: + ll, ur = bbox + margin = vector([11 * self.track_width] * 2) + self.bbox = [ll - margin, ur + margin] + # Dictionary for vdd and gnd pins self.pins = {} - # This is a set of all pins (ignoring names) so that can quickly - # not create blockages for pins - # (They will be blocked when we are routing other - # nets based on their name.) + # Set of all the pins self.all_pins = set() - - # The labeled pins above categorized into pin groups - # that are touching/connected. - self.pin_groups = {} - - # The blockage data structures - # A list of metal shapes (using the same pin_layout structure) - # that could be blockages. - # This will include the pins above as well. + # This is all the blockages including the pins. The graph class handles + # pins as blockages while considering their routability self.blockages = [] - # The corresponding set of blocked grids for above blockage pin_layout shapes - # It is a cached set of grids that *could* be blocked, but may be unblocked - # depending on which pin we are routing. - self.blocked_grids = set() + # This is all the vias between routing layers + self.vias = [] + # Fake pins are imaginary pins on the side supply pins to route other + # pins to them + self.fake_pins = [] - # The routed data structures - # A list of paths that have been "routed" - self.paths = [] - # A list of path blockages (they might be expanded for wide metal DRC) - self.path_blockages = [] + # Set the offset here + self.half_wire = snap(self.track_wire / 2) - # The perimeter pins should be placed outside the SRAM macro by a distance - self.margin = margin - self.init_bbox(bbox, margin) - # New pins if we create a ring or side pins or etc. - self.new_pins = {} + def prepare_gds_reader(self): + """ Write the current layout to a temporary file to read the layout. """ - def init_bbox(self, bbox=None, margin=0): - """ - Initialize the ll,ur values with the paramter or using the layout boundary. - """ - if not bbox: - self.bbox = self.cell.get_bbox(margin) - else: - self.bbox = bbox - - (self.ll, self.ur) = self.bbox - - def get_bbox(self): - return self.bbox - - def create_routing_grid(self, router_type=None): - """ - Create (or recreate) a sprase routing grid with A* expansion functions. - """ - debug.check(router_type or hasattr(self, "router_type"), "Must specify a routing grid type.") - - self.init_bbox(self.bbox, self.margin) - - if router_type: - self.router_type = router_type - self.rg = router_type(self.ll, self.ur, self.track_width) - else: - self.rg = self.router_type(self.ll, self.ur, self.track_width) - - def clear_pins(self): - """ - Convert the routed path to blockages. - Keep the other blockages unchanged. - """ - self.pins = {} - self.all_pins = set() - self.pin_groups = {} - # DO NOT clear the blockages as these don't change - self.rg.reinit() - - def set_top(self, top_name): - """ If we want to route something besides the top-level cell.""" - self.top_name = top_name - - def is_wave(self, path): - """ - Determines if this is a multi-track width wave (True) - # or a normal route (False) - """ - return len(path[0]) > 1 - - def retrieve_pins(self, pin_name): - """ - Retrieve the pin shapes on metal 3 from the layout. - """ - debug.info(2, "Retrieving pins for {}.".format(pin_name)) - shape_list = self.layout.getAllPinShapes(str(pin_name)) - pin_set = set() - for shape in shape_list: - (layer, boundary) = shape - # GDSMill boundaries are in (left, bottom, right, top) order - # so repack and snap to the grid - ll = vector(boundary[0], boundary[1]).snap_to_grid() - ur = vector(boundary[2], boundary[3]).snap_to_grid() - rect = [ll, ur] - pin = pin_layout(pin_name, rect, layer) - pin_set.add(pin) - - debug.check(len(pin_set) > 0, - "Did not find any pin shapes for {0}.".format(str(pin_name))) - - self.pins[pin_name] = pin_set - self.all_pins.update(pin_set) - - for pin in self.pins[pin_name]: - debug.info(3, "Retrieved pin {}".format(str(pin))) - - def find_blockages(self): - """ - Iterate through all the layers and write the obstacles to the routing grid. - This doesn't consider whether the obstacles will be pins or not. - They get reset later if they are not actually a blockage. - """ - debug.info(1, "Finding blockages.") - for lpp in [self.vert_lpp, self.horiz_lpp]: - self.retrieve_blockages(lpp) - - def find_pins_and_blockages(self, pin_list): - """ - Find the pins and blockages in the design - """ - - # If didn't specify a gds blockage file, write it out to read the gds - # This isn't efficient, but easy for now - # Load the gds file and read in all the shapes - self.cell.gds_write(self.gds_filename) + self.design.gds_write(self.gds_filename) self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(self.gds_filename) - self.top_name = self.layout.rootStructureName - # print_time("GDS read",datetime.now(), start_time) - # This finds the pin shapes and sorts them into "groups" that - # are connected. This must come before the blockages, so we - # can not count the pins themselves - # as blockages. - start_time = datetime.now() - for pin_name in pin_list: - self.retrieve_pins(pin_name) - print_time("Retrieving pins", datetime.now(), start_time, 4) - start_time = datetime.now() - for pin_name in pin_list: - self.analyze_pins(pin_name) - print_time("Analyzing pins", datetime.now(), start_time, 4) - - # This will get all shapes as blockages and convert to grid units - # This ignores shapes that were pins - start_time = datetime.now() - self.find_blockages() - print_time("Finding blockages", datetime.now(), start_time, 4) - - # Convert the blockages to grid units - start_time = datetime.now() - self.convert_blockages() - print_time("Converting blockages", datetime.now(), start_time, 4) - - # This will convert the pins to grid units - # It must be done after blockages to ensure no DRCs - # between expanded pins and blocked grids - start_time = datetime.now() - for pin in pin_list: - self.convert_pins(pin) - print_time("Converting pins", datetime.now(), start_time, 4) - - # Combine adjacent pins into pin groups to reduce run-time - # by reducing the number of maze routes. - # This algorithm is > O(n^2) so remove it for now - # start_time = datetime.now() - # for pin in pin_list: - # self.combine_adjacent_pins(pin) - # print_time("Combining adjacent pins",datetime.now(), start_time, 4) - - # Separate any adjacent grids of differing net names - # that overlap - # Must be done before enclosing pins - start_time = datetime.now() - self.separate_adjacent_pins(0) - print_time("Separating adjacent pins", datetime.now(), start_time, 4) - - # Enclose the continguous grid units in a metal - # rectangle to fix some DRCs - #start_time = datetime.now() - #self.enclose_pins() - #print_time("Enclosing pins", datetime.now(), start_time, 4) - - # MRG: Removing this code for now. The later compute enclosure code - # assumes that all pins are touching and this may produce sets of pins - # that are not connected. - # def combine_adjacent_pins(self, pin_name): - # """ - # Find pins that have adjacent routing tracks and merge them into a - # single pin_group. The pins themselves may not be touching, but - # enclose_pins in the next step will ensure they are touching. - # """ - # debug.info(1,"Combining adjacent pins for {}.".format(pin_name)) - # # Find all adjacencies - # adjacent_pins = {} - # for index1,pg1 in enumerate(self.pin_groups[pin_name]): - # for index2,pg2 in enumerate(self.pin_groups[pin_name]): - # # Cannot combine with yourself, also don't repeat - # if index1<=index2: - # continue - # # Combine if at least 1 grid cell is adjacent - # if pg1.adjacent(pg2): - # if not index1 in adjacent_pins: - # adjacent_pins[index1] = set([index2]) - # else: - # adjacent_pins[index1].add(index2) - - # # Make a list of indices to ensure every group gets in the new set - # all_indices = set([x for x in range(len(self.pin_groups[pin_name]))]) - - # # Now reconstruct the new groups - # new_pin_groups = [] - # for index1,index2_set in adjacent_pins.items(): - # # Remove the indices if they are added to the new set - # all_indices.discard(index1) - # all_indices.difference_update(index2_set) - - # # Create the combined group starting with the first item - # combined = self.pin_groups[pin_name][index1] - # # Add all of the other items that overlapped - # for index2 in index2_set: - # pg = self.pin_groups[pin_name][index2] - # combined.add_group(pg) - # debug.info(3,"Combining {0} {1}:".format(pin_name, index2)) - # debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins)) - # debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) - # new_pin_groups.append(combined) - - # # Add the pin groups that weren't added to the new set - # for index in all_indices: - # new_pin_groups.append(self.pin_groups[pin_name][index]) - - # old_size = len(self.pin_groups[pin_name]) - # # Use the new pin group! - # self.pin_groups[pin_name] = new_pin_groups - # removed_pairs = old_size - len(new_pin_groups) - # debug.info(1, - # "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) - - # return removed_pairs - - def separate_adjacent_pins(self, separation): + def merge_shapes(self, merger, shape_list): """ - This will try to separate all grid pins by the supplied - number of separation tracks (default is to prevent adjacency). - """ - pin_names = self.pin_groups.keys() - - for (pin_name1, pin_name2) in itertools.combinations(pin_names, 2): - self.separate_adjacent_pin(pin_name1, pin_name2, separation) - - def separate_adjacent_pin(self, pin_name1, pin_name2, separation): - """ - Go through all of the pin groups and check if any other pin group is - within a separation of it. - If so, reduce the pin group grid to not include the adjacent grid. - Try to do this intelligently to keep th pins enclosed. - """ - debug.info(2, - "Comparing {0} and {1} adjacency".format(pin_name1, - pin_name2)) - removed_grids = 0 - - for index1, pg1 in enumerate(self.pin_groups[pin_name1]): - for index2, pg2 in enumerate(self.pin_groups[pin_name2]): - adj_grids = pg1.adjacent_grids(pg2, separation) - removed_grids += len(adj_grids) - # These should have the same length, so... - if len(adj_grids) > 0: - debug.info(3, - "Adjacent grids {0} {1} adj={2}".format(index1, - index2, - adj_grids)) - self.remove_adjacent_grid(pg1, pg2, adj_grids) - - debug.info(2, "Removed {} adjacent grids.".format(removed_grids)) - - def remove_adjacent_grid(self, pg1, pg2, adj_grids): - """ - Remove one of the adjacent grids in a heuristic manner. - This will try to keep the groups similar sized by - removing from the bigger group. + Merge shapes in the list into the merger if they are contained or + aligned by the merger. """ - if pg1.size() > pg2.size(): - bigger = pg1 - smaller = pg2 - else: - bigger = pg2 - smaller = pg1 - - for adj in adj_grids: + merger_core = merger.get_core() + for shape in list(shape_list): + shape_core = shape.get_core() + # If merger contains the shape, remove it from the list + if merger_core.contains(shape_core): + shape_list.remove(shape) + # If the merger aligns with the shape, expand the merger and remove + # the shape from the list + elif merger_core.aligns(shape_core): + merger.bbox([shape]) + merger_core.bbox([shape_core]) + shape_list.remove(shape) - # If the adjacent grids are a subset of the secondary - # grids (i.e. not necessary) remove them from each - if adj in bigger.secondary_grids: - debug.info(3,"Removing {} from bigger secondary {}".format(adj, - bigger)) - bigger.grids.remove(adj) - bigger.secondary_grids.remove(adj) - self.blocked_grids.add(adj) - elif adj in smaller.secondary_grids: - debug.info(3,"Removing {} from smaller secondary {}".format(adj, - smaller)) - smaller.grids.remove(adj) - smaller.secondary_grids.remove(adj) - self.blocked_grids.add(adj) - else: - # If we couldn't remove from a secondary grid, - # we must remove from the primary - # grid of at least one pin - if adj in bigger.grids: - debug.info(3,"Removing {} from bigger primary {}".format(adj, - bigger)) - bigger.grids.remove(adj) - elif adj in smaller.grids: - debug.info(3,"Removing {} from smaller primary {}".format(adj, - smaller)) - smaller.grids.remove(adj) + def find_pins(self, pin_name): + """ Find the pins with the given name. """ + debug.info(2, "Finding all pins for {}".format(pin_name)) - def set_supply_rail_blocked(self, value): - # This is just a virtual function - pass - - def prepare_blockages(self, src=None, dest=None): - """ - Reset and add all of the blockages in the design. - Skip adding blockages from src and dest component if specified as a tuple of name,component. - """ - debug.info(3, "Preparing blockages.") - - # Start fresh. Not the best for run-time, but simpler. - self.clear_all_blockages() - - # This adds the initial blockges of the design - # which includes all blockages due to non-pin shapes - # print("BLOCKING:", self.blocked_grids) - self.set_blockages(self.blocked_grids, True) - - # Block all of the supply rails - # (some will be unblocked if they're a target) - try: - self.set_supply_rail_blocked(True) - except AttributeError: - # If function doesn't exist, it isn't a supply router - pass - - # Now go and block all of the blockages due to pin shapes. - # Some of these will get unblocked later if they are the source/target. - for name in self.pin_groups: - blockage_grids = [] - for component_idx, component in enumerate(self.pin_groups[name]): - # Skip adding source or dest blockages - if src and src[0] == name and src[1] == component_idx: - continue - if dest and dest[0] == name and dest[1] == component_idx: - continue - blockage_grids.extend(component.blockages) - self.set_blockages(blockage_grids, True) - - # If we have paths that were recently routed, add them as blockages as well. - # We might later do rip-up and reroute so they might not be metal shapes in the design yet. - # Also, this prevents having to reload an entire GDS and find the blockage shapes. - self.set_blockages(self.path_blockages) - - def convert_shape_to_units(self, shape): - """ - Scale a shape (two vector list) to user units - """ - unit_factor = [GDS["unit"][0]] * 2 - ll = shape[0].scale(unit_factor) - ur = shape[1].scale(unit_factor) - return [ll, ur] - - def min_max_coord(self, coord): - """ - Find the lowest and highest corner of a Rectangle - """ - coordinate = [] - minx = min(coord[0][0], coord[1][0], coord[2][0], coord[3][0]) - maxx = max(coord[0][0], coord[1][0], coord[2][0], coord[3][0]) - miny = min(coord[0][1], coord[1][1], coord[2][1], coord[3][1]) - maxy = max(coord[0][1], coord[1][1], coord[2][1], coord[3][1]) - coordinate += [vector(minx, miny)] - coordinate += [vector(maxx, maxy)] - return coordinate - - def get_inertia(self, p0, p1): - """ - Sets the direction based on the previous direction we came from. - """ - # direction (index) of movement - if p0.x != p1.x: - return 0 - elif p0.y != p1.y: - return 1 - else: - # z direction - return 2 - - def clear_blockages(self, pin_name): - """ - This function clears a given pin and all of its components from being blockages. - """ - blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.blockages} - self.set_blockages(blockage_grids, False) - - def clear_all_blockages(self): - """ - Clear all blockages on the grid. - """ - debug.info(3, "Clearing all blockages") - self.rg.clear_blockages() - - def set_blockages(self, blockages, value=True): - """ Flag the blockages in the grid """ - self.rg.set_blocked(blockages, value) - - def convert_to_tracks(self, ll, ur, z): - debug.info(3, "Converting ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - - grid_list = [] - for x in range(int(ll[0]), int(ur[0])+1): - for y in range(int(ll[1]), int(ur[1])+1): - grid_list.append(vector3d(x, y, z)) - - return set(grid_list) - - def convert_blockage(self, blockage): - """ - Convert a pin layout blockage shape to routing grid tracks. - """ - # Inflate the blockage by half a spacing rule - [ll, ur] = self.convert_shape_to_tracks(blockage.inflate()) - zlayer = self.get_zindex(blockage.lpp) - blockage_tracks = self.convert_to_tracks(ll, ur, zlayer) - return blockage_tracks - - def convert_blockages(self): - """ Convert blockages to grid tracks. """ - debug.info(1, "Converting blockages.") - for blockage in self.blockages: - debug.info(3, "Converting blockage {}".format(str(blockage))) - blockage_list = self.convert_blockage(blockage) - self.blocked_grids.update(blockage_list) - - def get_blocked_grids(self): - """ - Return the blocked grids with their flag set - """ - #return set([x for x in self.blocked_grids if self.rg.is_blocked(x)]) - # These are all the non-pin blockages - return self.blocked_grids - - def retrieve_blockages(self, lpp): - """ - Recursive find boundaries as blockages to the routing grid. - """ - - shapes = self.layout.getAllShapes(lpp) - for boundary in shapes: + shape_list = self.layout.getAllPinShapes(str(pin_name)) + pin_set = set() + for shape in shape_list: + layer, boundary = shape + # gdsMill boundaries are in (left, bottom, right, top) order ll = vector(boundary[0], boundary[1]) ur = vector(boundary[2], boundary[3]) rect = [ll, ur] - new_shape = pin_layout("blockage{}".format(len(self.blockages)), - rect, - lpp) + new_pin = graph_shape(pin_name, rect, layer) + # Skip this pin if it's contained by another pin of the same type + if new_pin.core_contained_by_any(pin_set): + continue + # Merge previous pins into this one if possible + self.merge_shapes(new_pin, pin_set) + pin_set.add(new_pin) + # Add these pins to the 'pins' dict + self.pins[pin_name] = pin_set + self.all_pins.update(pin_set) - # If there is a rectangle that is the same in the pins, - # it isn't a blockage! - if new_shape not in self.all_pins and not self.pin_contains(new_shape): + + def find_blockages(self, name="blockage"): + """ Find all blockages in the routing layers. """ + debug.info(2, "Finding blockages...") + + for lpp in [self.vert_lpp, self.horiz_lpp]: + shapes = self.layout.getAllShapes(lpp) + for boundary in shapes: + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) + rect = [ll, ur] + new_shape = graph_shape(name, rect, lpp) + new_shape = self.inflate_shape(new_shape) + # Skip this blockage if it's contained by a pin or an existing + # blockage + if new_shape.core_contained_by_any(self.all_pins) or \ + new_shape.core_contained_by_any(self.blockages): + continue + # Merge previous blockages into this one if possible + self.merge_shapes(new_shape, self.blockages) self.blockages.append(new_shape) - def pin_contains(self, shape): - for pin in self.all_pins: - if pin.contains(shape): - return True - return False - def convert_point_to_units(self, p): - """ - Convert a path set of tracks to center line path. - """ - pt = vector3d(p) - pt = pt.scale(self.track_widths[0], self.track_widths[1], 1) - return pt + def find_vias(self): + """ Find all vias in the routing layers. """ + debug.info(2, "Finding vias...") - def convert_wave_to_units(self, wave): - """ - Convert a wave to a set of center points - """ - return [self.convert_point_to_units(i) for i in wave] + # Prepare lpp values here + from openram.tech import layer + via_lpp = layer[self.via_layer_name] + valid_lpp = self.horiz_lpp - def convert_shape_to_tracks(self, shape): - """ - Convert a rectangular shape into track units. - """ - (ll, ur) = shape - ll = snap_to_grid(ll) - ur = snap_to_grid(ur) - - # to scale coordinates to tracks - debug.info(3, "Converting [ {0} , {1} ]".format(ll, ur)) - ll = ll.scale(self.track_factor) - ur = ur.scale(self.track_factor) - # We can round since we are using inflated shapes - # and the track points are at the center - ll = ll.round() - ur = ur.round() - return [ll, ur] - - def convert_pin_to_tracks(self, pin_name, pin, expansion=0): - """ - Convert a rectangular pin shape into a list of track locations,layers. - If no pins are "on-grid" (i.e. sufficient overlap) - it makes the one with most overlap if it is not blocked. - If expansion>0, expamine areas beyond the current pin - when it is blocked. - """ - (ll, ur) = pin.rect - debug.info(3, "Converting pin [ {0} , {1} ]".format(ll, ur)) - - # scale the size bigger to include neaby tracks - ll_scaled = ll.scale(self.track_factor).floor() - ur_scaled = ur.scale(self.track_factor).ceil() - - # Keep tabs on tracks with sufficient and insufficient overlap - sufficient_list = set() - insufficient_list = set() - - zindex = self.get_zindex(pin.lpp) - for x in range(int(ll_scaled[0]) - expansion, int(ur_scaled[0]) + 1 + expansion): - for y in range(int(ll_scaled[1] - expansion), int(ur_scaled[1]) + 1 + expansion): - cur_grid = vector3d(x, y, zindex) - (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, cur_grid) - if full_overlap: - sufficient_list.update([full_overlap]) - if partial_overlap: - insufficient_list.update([partial_overlap]) - debug.info(3, - "Converting [ {0} , {1} ] full={2}".format(x, - y, - full_overlap)) - - # Return all grids with any potential overlap (sufficient or not) - return (sufficient_list, insufficient_list) - - def get_all_offgrid_pin(self, pin, insufficient_list): - """ - Find a list of all pins with some overlap. - """ - # print("INSUFFICIENT LIST",insufficient_list) - # Find the coordinate with the most overlap - any_overlap = set() - for coord in insufficient_list: - full_pin = self.convert_track_to_pin(coord) - # Compute the overlap with that rectangle - overlap_rect = pin.compute_overlap(full_pin) - # Determine the max x or y overlap - max_overlap = max(overlap_rect) - if max_overlap > 0: - any_overlap.update([coord]) - - return any_overlap - - def get_best_offgrid_pin(self, pin, insufficient_list): - """ - Find a list of the single pin with the most overlap. - """ - # Find the coordinate with the most overlap - best_coord = None - best_overlap = -math.inf - for coord in insufficient_list: - full_pin = self.convert_track_to_pin(coord) - # Compute the overlap with that rectangle - overlap_rect = pin.compute_overlap(full_pin) - # Determine the min x or y overlap - min_overlap = min(overlap_rect) - if min_overlap > best_overlap: - best_overlap = min_overlap - best_coord = coord - - return set([best_coord]) - - def get_furthest_offgrid_pin(self, pin, insufficient_list): - """ - Get a grid cell that is the furthest from the blocked grids. - """ - - # Find the coordinate with the most overlap - best_coord = None - best_dist = math.inf - for coord in insufficient_list: - min_dist = grid_utils.distance_set(coord, self.blocked_grids) - if min_dist < best_dist: - best_dist = min_dist - best_coord = coord - - return set([best_coord]) - - def get_nearest_offgrid_pin(self, pin, insufficient_list): - """ - Given a pin and a list of grid cells (probably non-overlapping), - return the nearest grid cell (center to center). - """ - # Find the coordinate with the most overlap - best_coord = None - best_dist = math.inf - for coord in insufficient_list: - track_pin = self.convert_track_to_pin(coord) - min_dist = pin.distance(track_pin) - if min_dist < best_dist: - best_dist = min_dist - best_coord = coord - - return set([best_coord]) - - def break_on_grids(self, tracks, xvals, yvals): - track_list = [] - for x in xvals: - for y in yvals: - track_list.append(vector3d(x, y, 0)) - track_list.append(vector3d(x, y, 1)) - - for current in tracks: - if current in track_list: - breakpoint() - - def divide_pin_to_tracks(self, pin, tracks): - """ - Return a list of pin shape parts that are in the tracks. - """ - # If pin is smaller than a track, just return it. - track_pin = self.convert_track_to_shape_pin(list(tracks)[0]) - if pin.width() < track_pin.width() and pin.height() < track_pin.height(): - return [pin] - - overlap_pins = [] - for track in tracks: - track_pin = self.convert_track_to_shape_pin(track) - overlap_pin = track_pin.intersection(pin) - - # If pin is smaller than minwidth, in one dimension, skip it. - min_pin_width = drc("minwidth_{0}". format(pin.layer)) - if not overlap_pin or (overlap_pin.width() < min_pin_width and overlap_pin.height() < min_pin_width): + shapes = self.layout.getAllShapes(via_lpp) + for boundary in shapes: + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) + rect = [ll, ur] + new_shape = graph_shape("via", rect, valid_lpp) + # Skip this via if it's contained by an existing via blockage + if new_shape.contained_by_any(self.vias): continue - else: - overlap_pins.append(overlap_pin) - - debug.check(len(overlap_pins) > 0, "No pins overlapped the tracks.") - - return overlap_pins + self.vias.append(self.inflate_shape(new_shape)) - def convert_pin_coord_to_tracks(self, pin, coord): - """ - Return all tracks that an inflated pin overlaps - """ - # This is using the full track shape rather - # than a single track pin shape - # because we will later patch a connector if there isn't overlap. - track_pin = self.convert_track_to_shape_pin(coord) + def convert_vias(self): + """ Convert vias that overlap a pin. """ - # This is the normal pin inflated by a minimum design rule - inflated_pin = pin_layout(pin.name, - pin.inflate(0.5 * self.track_space), - pin.layer) - - overlap_length = pin.overlap_length(track_pin) - debug.info(4,"Check overlap: {0} {1} . {2} = {3}".format(coord, - pin.rect, - track_pin, - overlap_length)) - inflated_overlap_length = inflated_pin.overlap_length(track_pin) - debug.info(4,"Check overlap: {0} {1} . {2} = {3}".format(coord, - inflated_pin.rect, - track_pin, - inflated_overlap_length)) - - # If it overlaps with the pin, it is sufficient - if overlap_length == math.inf or overlap_length > 0: - debug.info(4," Overlap: {0} >? {1}".format(overlap_length, 0)) - return (coord, None) - # If it overlaps with the inflated pin, it is partial - elif inflated_overlap_length == math.inf or inflated_overlap_length > 0: - debug.info(4," Partial overlap: {0} >? {1}".format(inflated_overlap_length, 0)) - return (None, coord) - else: - debug.info(4, " No overlap: {0} {1}".format(overlap_length, 0)) - return (None, None) - - def convert_track_to_pin(self, track): - """ - Convert a grid point into a rectangle shape that is centered - track in the track and leaves half a DRC space in each direction. - """ - # calculate lower left - x = track.x * self.track_width - 0.5 * self.track_width + 0.5 * self.track_space - y = track.y * self.track_width - 0.5 * self.track_width + 0.5 * self.track_space - ll = snap_to_grid(vector(x,y)) - - # calculate upper right - x = track.x * self.track_width + 0.5 * self.track_width - 0.5 * self.track_space - y = track.y * self.track_width + 0.5 * self.track_width - 0.5 * self.track_space - ur = snap_to_grid(vector(x, y)) - - p = pin_layout("", [ll, ur], self.get_layer(track[2])) - return p - - def convert_tracks_to_pin(self, tracks): - """ - Convert a list of grid point into a rectangle shape. - Must all be on the same layer. - """ - for t in tracks: - debug.check(t[2] == tracks[0][2], "Different layers used.") - - # For each shape, convert it to a pin - pins = [self.convert_track_to_pin(t) for t in tracks] - # Now find the bounding box - minx = min([p.lx() for p in pins]) - maxx = max([p.rx() for p in pins]) - miny = min([p.by() for p in pins]) - maxy = max([p.uy() for p in pins]) - ll = vector(minx, miny) - ur = vector(maxx, maxy) - - p = pin_layout("", [ll, ur], self.get_layer(tracks[0][2])) - return p - - def convert_track_to_shape_pin(self, track): - """ - Convert a grid point into a rectangle shape - that occupies the entire centered track. - """ - # to scale coordinates to tracks - x = track[0]*self.track_width - 0.5*self.track_width - y = track[1]*self.track_width - 0.5*self.track_width - # offset lowest corner object to to (-track halo,-track halo) - ll = snap_to_grid(vector(x, y)) - ur = snap_to_grid(ll + vector(self.track_width, self.track_width)) - - p = pin_layout("", [ll, ur], self.get_layer(track[2])) - return p - - def convert_track_to_shape(self, track): - """ - Convert a grid point into a rectangle shape - that occupies the entire centered track. - """ - # to scale coordinates to tracks - try: - x = track[0]*self.track_width - 0.5*self.track_width - except TypeError: - debug.warning("{} {} {} {}".format(track[0], type(track[0]), self.track_width, type(self.track_width))) - y = track[1]*self.track_width - 0.5*self.track_width - # offset lowest corner object to to (-track halo,-track halo) - ll = snap_to_grid(vector(x, y)) - ur = snap_to_grid(ll + vector(self.track_width, self.track_width)) - - return [ll, ur] - - def convert_track_to_inflated_pin(self, track): - """ - Convert a grid point into a rectangle shape - that is inflated by a half DRC space. - """ - # calculate lower left - x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space - y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space - ll = snap_to_grid(vector(x,y)) - - # calculate upper right - x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space - y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space - ur = snap_to_grid(vector(x, y)) - - p = pin_layout("", [ll, ur], self.get_layer(track[2])) - return p - - def analyze_pins(self, pin_name): - """ - Analyze the shapes of a pin and combine - them into pin_groups which are connected. - """ - debug.info(2, "Analyzing pin groups for {}.".format(pin_name)) - pin_set = self.pins[pin_name] - - # This will be a list of pin tuples that overlap - overlap_list = [] - - # Sort the rectangles into a list with lower/upper y coordinates - bottom_y_coordinates = [(x.by(), x, "bottom") for x in pin_set] - top_y_coordinates = [(x.uy(), x, "top") for x in pin_set] - y_coordinates = bottom_y_coordinates + top_y_coordinates - y_coordinates.sort(key=lambda x: x[0]) - - # Map the pins to the lower indices - bottom_index_map = {x[1]: i for i, x in enumerate(y_coordinates) if x[2] == "bottom"} - # top_index_map = {x[1]: i for i, x in enumerate(y_coordinates) if x[2] == "bottom"} - - # Sort the pin list by x coordinate - pin_list = list(pin_set) - pin_list.sort(key=lambda x: x.lx()) - - # for shapes in x order - for pin in pin_list: - # start at pin's lower y coordinate - bottom_index = bottom_index_map[pin] - compared_pins = set() - for i in range(bottom_index, len(y_coordinates)): - compare_pin = y_coordinates[i][1] - # Don't overlap yourself - if pin == compare_pin: - continue - # Done when we encounter any shape above the pin - if compare_pin.by() > pin.uy(): + for via in self.vias: + via_core = via.get_core() + for pin in self.all_pins: + pin_core = pin.get_core() + via_core.lpp = pin_core.lpp + # If the via overlaps a pin, change its name + if via_core.overlaps(pin_core): + via.rename(pin.name) break - # Don't double compare the same pin twice - if compare_pin in compared_pins: - continue - compared_pins.add(compare_pin) - # If we overlap, add them to the list - if pin.overlaps(compare_pin): - overlap_list.append((pin, compare_pin)) - # Initial unique group assignments - group_id = {} - gid = 1 - for pin in pin_list: - group_id[pin] = gid - gid += 1 - for p in overlap_list: - (p1, p2) = p - for pin in pin_list: - if group_id[pin] == group_id[p2]: - group_id[pin] = group_id[p1] + def convert_blockages(self): + """ Convert blockages that overlap a pin. """ - # For each pin add it to it's group - group_map = {} - for pin in pin_list: - gid = group_id[pin] - if gid not in group_map: - group_map[gid] = pin_group(name=pin_name, - pin_set=[], - router=self) - group_map[gid].add_pin(pin) + # NOTE: You need to run `convert_vias()` before since a blockage may + # be connected to a pin through a via. + for blockage in self.blockages: + blockage_core = blockage.get_core() + for pin in self.all_pins: + pin_core = pin.get_core() + # If the blockage overlaps a pin, change its name + if blockage_core.overlaps(pin_core): + blockage.rename(pin.name) + break + else: + for via in self.vias: + # Skip if this via isn't connected to a pin + if via.name == "via": + continue + via_core = via.get_core() + via_core.lpp = blockage_core.lpp + # If the blockage overlaps a pin via, change its name + if blockage_core.overlaps(via_core): + blockage.rename(via.name) + break - self.pin_groups[pin_name] = list(group_map.values()) - def convert_pins(self, pin_name): - """ - Convert the pin groups into pin tracks and blockage tracks. - """ - debug.info(2, "Converting pins for {}.".format(pin_name)) - for pg in self.pin_groups[pin_name]: - pg.convert_pin() + def inflate_shape(self, shape): + """ Inflate a given shape with spacing rules. """ - def enclose_pins(self): - """ - This will find the biggest rectangle enclosing some grid squares and - put a rectangle over it. It does not enclose grid squares - that are blocked by other shapes. - """ - for pin_name in self.pin_groups: - debug.info(2, "Enclosing pins for {}".format(pin_name)) - for pg in self.pin_groups[pin_name]: - self.clear_blockages(pin_name) - pg.enclose_pin() - pg.add_enclosure(self.cell) - - def add_source(self, pin_name): - """ - This will mark the grids for all pin components as a source. - Marking as source or target also clears blockage status. - """ - self.source_name = pin_name - self.source_components = [] - for i in range(self.num_pin_components(pin_name)): - self.add_pin_component_source(pin_name, i) - - # Clearing the blockage of this pin requires the inflated pins - self.clear_blockages(pin_name) - - def add_target(self, pin_name): - """ - This will mark the grids for all pin components as a target. - Marking as source or target also clears blockage status. - """ - self.target_name = pin_name - self.target_components = [] - for i in range(self.num_pin_components(pin_name)): - self.add_pin_component_target(pin_name, i) - - # Clearing the blockage of this pin requires the inflated pins - self.clear_blockages(pin_name) - - def add_side_supply_pin(self, name, side="left", width=3, space=2): - """ - Adds a supply pin to the perimeter and resizes the bounding box. - """ - pg = pin_group(name, [], self) - # Offset two spaces inside and one between the rings - if name == "gnd": - offset = width + 2 * space + # Get the layer-specific spacing rule + if self.get_zindex(shape.lpp) == 1: + spacing = self.vert_layer_spacing else: - offset = space - if side in ["left", "right"]: - layers = [1] - else: - layers = [0] + spacing = self.horiz_layer_spacing + # If the shape is wider than the supply wire width, its spacing can be + # different + wide = min(shape.width(), shape.height()) + if wide > self.layer_widths[0]: + spacing = self.get_layer_space(self.get_zindex(shape.lpp), wide) - pg.grids = set(self.rg.get_perimeter_list(side=side, - width=width, - margin=self.margin, - offset=offset, - layers=layers)) - pg.enclosures = pg.compute_enclosures() - pg.pins = set(pg.enclosures) - debug.check(len(pg.pins)==1, "Too many pins for a side supply.") + # Shapes must keep their center lines away from any blockage to prevent + # the nodes from being unconnected + xdiff = self.track_wire - shape.width() + ydiff = self.track_wire - shape.height() + diff = snap(max(xdiff, ydiff) / 2) + if diff > 0: + spacing += diff - self.cell.pin_map[name].update(pg.pins) - self.pin_groups[name].append(pg) + # Add minimum unit to the spacing to keep nodes out of inflated regions + spacing += drc["grid"] - self.new_pins[name] = pg.pins + return shape.inflated_pin(spacing=spacing, + extra_spacing=self.half_wire) - def add_ring_supply_pin(self, name, width=3, space=3): + + def add_path(self, path): + """ Add the route path to the layout. """ + + nodes = self.prepare_path(path) + shapes = self.add_route(nodes) + return shapes + + + def prepare_path(self, path): """ - Adds a ring supply pin that goes outside the given bbox. - """ - pg = pin_group(name, [], self) - - # LEFT - left_grids = set(self.rg.get_perimeter_list(side="left_ring", - width=width, - margin=self.margin, - offset=space, - layers=[1])) - - # RIGHT - right_grids = set(self.rg.get_perimeter_list(side="right_ring", - width=width, - margin=self.margin, - offset=space, - layers=[1])) - # TOP - top_grids = set(self.rg.get_perimeter_list(side="top_ring", - width=width, - margin=self.margin, - offset=space, - layers=[0])) - # BOTTOM - bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring", - width=width, - margin=self.margin, - offset=space, - layers=[0])) - - horizontal_layer_grids = left_grids | right_grids - - # Must move to the same layer to find layer 1 corner grids - vertical_layer_grids = set() - for x in top_grids | bottom_grids: - vertical_layer_grids.add(vector3d(x.x, x.y, 1)) - - # Add vias in the overlap points - horizontal_corner_grids = vertical_layer_grids & horizontal_layer_grids - corners = [] - for g in horizontal_corner_grids: - self.add_via(g) - - # The big pin group, but exclude the corners from the pins - pg.grids = (left_grids | right_grids | top_grids | bottom_grids) - pg.enclosures = pg.compute_enclosures() - pg.pins = set(pg.enclosures) - - self.cell.pin_map[name].update(pg.pins) - self.pin_groups[name].append(pg) - self.new_pins[name] = pg.pins - - # Update the bbox so that it now includes the new pins - for p in pg.pins: - if p.lx() < self.ll.x or p.by() < self.ll.y: - self.ll = p.ll() - if p.rx() > self.ur.x or p.uy() > self.ur.y: - self.ur = p.ur() - self.bbox = (self.ll, self.ur) - self.create_routing_grid() - - def get_new_pins(self, name): - return self.new_pins[name] - - def add_perimeter_target(self, side="all"): - """ - This will mark all the cells on the perimeter of the original layout as a target. - """ - self.target_name = "" - self.target_components = [] - self.rg.add_perimeter_target(side=side) - - def num_pin_components(self, pin_name): - """ - This returns how many disconnected pin components there are. - """ - return len(self.pin_groups[pin_name]) - - def set_pin_component_source(self, pin_name, index): - """ - Add the pin component but also set it as the exclusive one. - Used by supply routing with multiple components. - """ - self.source_name = pin_name - self.source_components = [] - self.add_pin_component_source(pin_name, index) - - def add_pin_component_source(self, pin_name, index): - """ - This will mark only the pin tracks - from the indexed pin component as a source. - It also unsets it as a blockage. - """ - debug.check(index 1: - self.cell.add_route(layers=self.layers, - coordinates=abs_path, - layer_widths=self.layer_widths) - else: - self.cell.add_path(layer=self.layers[0], - coordinates=abs_path, - width=self.layer_widths[0]) - - def create_route_connector(self, path_tracks, pin_name, components): - """ - Find a rectangle to connect the track and the off-grid pin of a component. + Remove unnecessary nodes on the path to reduce the number of shapes in + the layout. """ - if len(path_tracks) == 0 or len(components) == 0: - return + last_added = path[0] + nodes = [path[0]] + direction = path[0].get_direction(path[1]) + candidate = path[1] + for i in range(2, len(path)): + node = path[i] + current_direction = node.get_direction(candidate) + # Skip the previous candidate since the current node follows the + # same direction + if direction == current_direction: + candidate = node + else: + last_added = candidate + nodes.append(candidate) + direction = current_direction + candidate = node + if candidate not in nodes: + nodes.append(candidate) + return nodes - # Find the track pin - track_pins = [self.convert_tracks_to_pin(x) for x in path_tracks] - # Convert the off-grid pin into parts in each routing grid - offgrid_pin_parts = [] - for component in components: - pg = self.pin_groups[pin_name][component] - for pin in pg.pins: - # Layer min with - min_width = drc("minwidth_{}".format(pin.layer)) - - # If we intersect, by a min_width, we are done! - for track_pin in track_pins: - intersection = pin.intersection(track_pin) - if intersection and intersection.width() > min_width and intersection.height() > min_width: - return - - #self.break_on_grids(pg.grids, xvals=[68], yvals=range(93,100)) - partial_pin_parts = self.divide_pin_to_tracks(pin, pg.grids) - offgrid_pin_parts.extend(partial_pin_parts) - - debug.check(len(offgrid_pin_parts) > 0, "No offgrid pin parts found.") - - # Find closest part - closest_track_pin, closest_part_pin = self.find_closest_pin(track_pins, offgrid_pin_parts) - - debug.check(closest_track_pin and closest_part_pin, "Found no closest pins.") - - # Find the bbox of the on-grid track and the off-grid pin part - closest_track_pin.bbox([closest_part_pin]) - - # Connect to off grid pin to track pin with closest shape - self.cell.add_rect(layer=closest_track_pin.layer, - offset=closest_track_pin.ll(), - width=closest_track_pin.width(), - height=closest_track_pin.height()) - - def find_closest_pin(self, first_list, second_list): + def add_route(self, nodes): """ - Find the closest pin in the lists. Does a stupid O(n^2). - """ - min_dist = None - min_item = (None, None) - for pin1 in first_list: - for pin2 in second_list: - if pin1.layer != pin2.layer: - continue - new_dist = pin1.distance(pin2) - if min_dist == None or new_dist < min_dist: - min_item = (pin1, pin2) - min_dist = new_dist - - return min_item - - - def add_single_enclosure(self, track): - """ - Add a metal enclosure that is the size of - the routing grid minus a spacing on each side. - """ - pin = self.convert_track_to_pin(track) - (ll, ur) = pin.rect - self.cell.add_rect(layer=self.get_layer(track.z), - offset=ll, - width=ur.x-ll.x, - height=ur.y-ll.y) - - def add_via(self, loc, size=1): - """ - Add a via centered at the current location - """ - loc = self.convert_point_to_units(vector3d(loc[0], loc[1], 0)) - self.cell.add_via_center(layers=self.layers, - offset=vector(loc.x, loc.y), - size=(size, size)) - - def compute_pin_enclosure(self, ll, ur, zindex, name=""): - """ - Enclose the tracks from ll to ur in a single rectangle that meets - the track DRC rules. - """ - layer = self.get_layer(zindex) - - # This finds the pin shape enclosed by the - # track with DRC spacing on the sides - pin = self.convert_track_to_pin(ll) - (abs_ll, unused) = pin.rect - pin = self.convert_track_to_pin(ur) - (unused, abs_ur) = pin.rect - - pin = pin_layout(name, [abs_ll, abs_ur], layer) - - return pin - - def contract_path(self, path): - """ - Remove intermediate points in a rectilinear path or a wave. - """ - # Waves are always linear, so just return the first and last. - if self.is_wave(path): - return [path[0], path[-1]] - - # Make a list only of points that change inertia of the path - newpath = [path[0]] - for i in range(1, len(path) - 1): - prev_inertia = self.get_inertia(path[i-1][0], path[i][0]) - next_inertia = self.get_inertia(path[i][0], path[i+1][0]) - # if we switch directions, add the point, otherwise don't - if prev_inertia != next_inertia: - newpath.append(path[i]) - - # always add the last path unless it was a single point - if len(path) > 1: - newpath.append(path[-1]) - return newpath - - def run_router(self, detour_scale): - """ - This assumes the blockages, source, and target are all set up. + Custom `add_route` function since `hierarchy_layout.add_route` isn't + working for this router. """ - # Double check source and taget are not same node, if so, we are done! - for k, v in self.rg.map.items(): - if v.source and v.target: - self.paths.append([k]) - return True + new_shapes = [] + for i in range(0, len(nodes) - 1): + start = nodes[i].center + end = nodes[i + 1].center + direction = nodes[i].get_direction(nodes[i + 1]) + diff = start - end + offset = start.min(end) + offset = vector(offset.x - self.half_wire, + offset.y - self.half_wire) + if direction == (1, 1): # Via + offset = vector(start.x, start.y) + self.design.add_via_center(layers=self.layers, + offset=offset) + else: # Wire + shape = self.design.add_rect(layer=self.get_layer(start.z), + offset=offset, + width=abs(diff.x) + self.track_wire, + height=abs(diff.y) + self.track_wire) + new_shapes.append(shape) + return new_shapes - # returns the path in tracks - (path, cost) = self.rg.route(detour_scale) - if path: - debug.info(2, "Found path: cost={0} {1}".format(cost, str(path))) - self.paths.append(grid_utils.flatten_set(path)) - self.add_route(path) - self.create_route_connector(path, - self.source_name, - self.source_components) - self.create_route_connector(path, - self.target_name, - self.target_components) - self.path_blockages.append(self.paths[-1]) - #self.write_debug_gds("debug_route.gds", False) - #breakpoint() - return True - else: - return False + def write_debug_gds(self, gds_name, g=None, source=None, target=None): + """ Write the debug GDSII file for the router. """ - def annotate_pin_and_tracks(self, pin, tracks): - """" - Annotate some shapes for debug purposes - """ - debug.info(0, "Annotating\n pin {0}\n tracks {1}".format(pin, tracks)) - for coord in tracks: - (ll, ur) = self.convert_track_to_shape(coord) - self.cell.add_rect(layer="text", - offset=ll, - width=ur[0] - ll[0], - height=ur[1] - ll[1]) - # (ll, ur) = self.convert_track_to_pin(coord).rect - # self.cell.add_rect(layer="boundary", - # offset=ll, - # width=ur[0] - ll[0], - # height=ur[1] - ll[1]) - (ll, ur) = pin.rect - self.cell.add_rect(layer="text", - offset=ll, - width=ur[0] - ll[0], - height=ur[1] - ll[1]) - - def write_debug_gds(self, gds_name="debug_route.gds", stop_program=True): - """ - Write out a GDS file with the routing grid and - search information annotated on it. - """ - debug.info(0, "Writing annotated router gds file to {}".format(gds_name)) - self.add_router_info() - self.cell.gds_write(gds_name) + self.add_router_info(g, source, target) + self.design.gds_write(gds_name) self.del_router_info() - if stop_program: - import sys - sys.exit(1) - def annotate_grid(self, g): + def add_router_info(self, g=None, source=None, target=None): """ - Display grid information in the GDS file for a single grid cell. + Add debug information to the text layer about the graph and router. """ - shape = self.convert_track_to_shape(g) - partial_track = vector(0, self.track_width / 6.0) - self.cell.add_rect(layer="text", - offset=shape[0], - width=shape[1].x - shape[0].x, - height=shape[1].y - shape[0].y) - t = self.rg.map[g].get_type() - # midpoint offset - off = vector((shape[1].x + shape[0].x) / 2, - (shape[1].y + shape[0].y) / 2) - if t: - if g[2] == 1: - # Upper layer is upper right label - type_off = off + partial_track - else: - # Lower layer is lower left label - type_off = off - partial_track - self.cell.add_label(text=str(t), - layer="text", - offset=type_off) + # Display the inflated blockage + if g: + for blockage in self.blockages: + if blockage in g.graph_blockages: + self.add_object_info(blockage, "blockage{}++[{}]".format(self.get_zindex(blockage.lpp), blockage.name)) + else: + self.add_object_info(blockage, "blockage{}[{}]".format(self.get_zindex(blockage.lpp), blockage.name)) + for node in g.nodes: + offset = (node.center.x, node.center.y) + self.design.add_label(text="n{}".format(node.center.z), + layer="text", + offset=offset) + else: + for blockage in self.blockages: + self.add_object_info(blockage, "blockage{}".format(self.get_zindex(blockage.lpp))) + for pin in self.fake_pins: + self.add_object_info(pin, "fake") + if source: + self.add_object_info(source, "source") + if target: + self.add_object_info(target, "target") - t = self.rg.map[g].get_cost() - partial_track = vector(self.track_width/6.0, 0) - if t: - if g[2] == 1: - # Upper layer is right label - type_off = off + partial_track - else: - # Lower layer is left label - type_off = off - partial_track - self.cell.add_label(text=str(t), - layer="text", - offset=type_off) - - self.cell.add_label(text="{0},{1}".format(g[0], g[1]), - layer="text", - offset=shape[0]) def del_router_info(self): - """ - Erase all of the comments on the current level. - """ - debug.info(2, "Erasing router info") - lpp = techlayer["text"] - self.cell.objs = [x for x in self.cell.objs if x.lpp != lpp] + """ Delete router information from the text layer. """ - def add_router_info(self): - """ - Write the routing grid and router cost, blockage, pins on - the boundary layer for debugging purposes. This can only be - called once or the labels will overlap. - """ - debug.info(2, "Adding router info") - - show_bbox = False - show_blockages = False - show_blockage_grids = False - show_enclosures = False - show_all_grids = True - - if show_bbox: - self.cell.add_rect(layer="text", - offset=vector(self.ll.x, self.ll.y), - width=self.ur.x - self.ll.x, - height=self.ur.y - self.ll.y) - - if show_all_grids: - for g in self.rg.map: - self.annotate_grid(g) - - if show_blockages: - # Display the inflated blockage - for blockage in self.blockages: - debug.info(1, "Adding {}".format(blockage)) - (ll, ur) = blockage.inflate() - self.cell.add_rect(layer="text", - offset=ll, - width=ur.x - ll.x, - height=ur.y - ll.y) - if show_blockage_grids: - self.set_blockages(self.blocked_grids, True) - for g in self.rg.map: - self.annotate_grid(g) - - if show_enclosures: - for key in self.pin_groups: - for pg in self.pin_groups[key]: - if not pg.enclosed: - continue - for pin in pg.enclosures: - # print("enclosure: ", - # pin.name, - # pin.ll(), - # pin.width(), - # pin.height()) - self.cell.add_rect(layer="text", - offset=pin.ll(), - width=pin.width(), - height=pin.height()) - - def get_perimeter_pin(self): - """ Return the shape of the last routed path that was on the perimeter """ - lastpath = self.paths[-1] - for v in lastpath: - if self.rg.is_target(v): - # Find neighboring grid to make double wide pin - neighbor = v + vector3d(0, 1, 0) - if neighbor in lastpath: - return self.convert_tracks_to_pin([v, neighbor]) - neighbor = v + vector3d(0, -1, 0) - if neighbor in lastpath: - return self.convert_tracks_to_pin([v, neighbor]) - neighbor = v + vector3d(1, 0, 0) - if neighbor in lastpath: - return self.convert_tracks_to_pin([v, neighbor]) - neighbor = v + vector3d(-1, 0, 0) - if neighbor in lastpath: - return self.convert_tracks_to_pin([v, neighbor]) - - # Else if we came from a different layer, we can only add - # a signle grid - return self.convert_track_to_pin(v) - - return None - - def get_ll_pin(self, pin_name): - """ Return the lowest, leftest pin group """ - - keep_pin = None - for index, pg in enumerate(self.pin_groups[pin_name]): - for pin in pg.enclosures: - if not keep_pin: - keep_pin = pin - else: - if pin.lx() <= keep_pin.lx() and pin.by() <= keep_pin.by(): - keep_pin = pin - - return keep_pin - - def check_all_routed(self, pin_name): - """ - Check that all pin groups are routed. - """ - for pg in self.pin_groups[pin_name]: - if not pg.is_routed(): - return False + lpp = tech_layer["text"] + self.design.objs = [x for x in self.design.objs if x.lpp != lpp] -# FIXME: This should be replaced with vector.snap_to_grid at some point -def snap_to_grid(offset): - """ - Changes the coodrinate to match the grid settings - """ - xoff = snap_val_to_grid(offset[0]) - yoff = snap_val_to_grid(offset[1]) - return vector(xoff, yoff) + def add_object_info(self, obj, label): + """ Add debug information to the text layer about an object. """ - -def snap_val_to_grid(x): - grid = drc("grid") - xgrid = int(round(round((x / grid), 2), 0)) - xoff = xgrid * grid - return xoff + ll, ur = obj.rect + self.design.add_rect(layer="text", + offset=ll, + width=ur.x - ll.x, + height=ur.y - ll.y) + self.design.add_label(text=label, + layer="text", + offset=ll) diff --git a/compiler/router/router_tech.py b/compiler/router/router_tech.py index 6c8c7d4c..af889a1e 100644 --- a/compiler/router/router_tech.py +++ b/compiler/router/router_tech.py @@ -67,6 +67,11 @@ class router_tech: self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size) self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size) + # Update spacing for the new widths + max_width = max(self.vert_layer_minwidth, self.horiz_layer_minwidth) + self.vert_layer_spacing = self.get_layer_space(1, max_width) + self.horiz_layer_spacing = self.get_layer_space(0, max_width) + self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing @@ -109,23 +114,34 @@ class router_tech: else: debug.error("Invalid zindex {}".format(zindex), -1) + def get_lpp(self, zindex): + if zindex == 1: + return self.vert_lpp + elif zindex == 0: + return self.horiz_lpp + else: + debug.error("Invalid zindex {}".format(zindex), -1) + def get_layer_width_space(self, zindex): """ These are the width and spacing of a supply layer given a supply rail of the given number of min wire widths. """ - if zindex==1: - layer_name = self.vert_layer_name - elif zindex==0: - layer_name = self.horiz_layer_name - else: - debug.error("Invalid zindex for track", -1) - - min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) - - min_width = self.route_track_width * drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf) - min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf) - + min_width = self.get_layer_width(zindex) + min_spacing = self.get_layer_space(zindex, min_width) return (min_width, min_spacing) + def get_layer_width(self, zindex): + """ Return the minimum width of a layer. """ + layer_name = self.get_layer(zindex) + min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) + min_width = self.route_track_width * drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf) + return min_width + def get_layer_space(self, zindex, width=None): + """ Return the minimum spacing of a layer given wire width. """ + if width is None: + width = self.get_layer_width(zindex) + layer_name = self.get_layer(zindex) + min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * width, math.inf) + return min_spacing diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 4b57cd18..5e4ae66e 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -1,104 +1,169 @@ # See LICENSE for licensing information. # -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz # All rights reserved. # -from datetime import datetime from openram import debug -from openram import print_time +from openram.base.vector import vector +from openram import OPTS +from .graph import graph +from .graph_shape import graph_shape from .router import router -from .signal_grid import signal_grid class signal_escape_router(router): """ - A router that routes signals to perimeter and makes pins. + This is the signal escape router that uses the Hanan grid graph method. """ - def __init__(self, layers, design, bbox=None, margin=0): + def __init__(self, layers, design, bbox=None): + + # `router` is the base router class + router.__init__(self, layers, design, bbox) + + # New pins are the side supply pins + self.new_pins = {} + + + def route(self, pin_names): + """ Route the given pins to the perimeter. """ + debug.info(1, "Running signal escape router...") + + # Prepare gdsMill to find pins and blockages + self.prepare_gds_reader() + + # Find pins to be routed + for name in pin_names: + self.find_pins(name) + + # Find blockages and vias + self.find_blockages() + self.find_vias() + + # Convert blockages and vias if they overlap a pin + self.convert_vias() + self.convert_blockages() + + # Add fake pins on the perimeter to do the escape routing on + self.add_perimeter_fake_pins() + + # Add vdd and gnd pins as blockages as well + # NOTE: This is done to make vdd and gnd pins DRC-safe + for pin in self.all_pins: + self.blockages.append(self.inflate_shape(pin)) + + # Route vdd and gnd + for source, target, _ in self.get_route_pairs(pin_names): + # Change fake pin's name so the graph will treat it as routable + target.name = source.name + # This is the routing region scale + scale = 1 + while True: + # Create the graph + g = graph(self) + region = g.create_graph(source, target, scale) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If there is no path found, exponentially try again with a + # larger routing region + if path is None: + rll, rur = region + bll, bur = self.bbox + # Stop scaling the region and throw an error + if rll.x < bll.x and rll.y < bll.y and \ + rur.x > bur.x and rur.y > bur.y: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Exponentially scale the region + scale *= 2 + debug.info(0, "Retry routing in larger routing region with scale {}".format(scale)) + continue + # Create the path shapes on layout + new_shapes = self.add_path(path) + self.new_pins[source.name] = new_shapes[0] + # Find the recently added shapes + self.prepare_gds_reader() + self.find_blockages(name) + self.find_vias() + break + self.replace_layout_pins() + + + def add_perimeter_fake_pins(self): """ - This will route on layers in design. It will get the blockages from - either the gds file name or the design itself (by saving to a gds file). + Add the fake pins on the perimeter to where the signals will be routed. """ - router.__init__(self, - layers=layers, - design=design, - bbox=bbox, - margin=margin) - def perimeter_dist(self, pin_name): - """ - Return the shortest Manhattan distance to the bounding box perimeter. - """ - loc = self.cell.get_pin(pin_name).center() - x_dist = min(loc.x - self.ll.x, self.ur.x - loc.x) - y_dist = min(loc.y - self.ll.y, self.ur.y - loc.y) + ll, ur = self.bbox + wide = self.track_wire - return min(x_dist, y_dist) + for side in ["top", "bottom", "left", "right"]: + vertical = side in ["left", "right"] - def escape_route(self, pin_names): - """ - Takes a list of tuples (name, side) and routes them. After routing, - it removes the old pin and places a new one on the perimeter. - """ - self.create_routing_grid(signal_grid) + # Calculate the lower left coordinate + if side == "top": + offset = vector(ll.x, ur.y - wide) + elif side == "bottom": + offset = vector(ll.x, ll.y) + elif side == "left": + offset = vector(ll.x, ll.y) + elif side == "right": + offset = vector(ur.x - wide, ll.y) - start_time = datetime.now() - self.find_pins_and_blockages(pin_names) - print_time("Finding pins and blockages",datetime.now(), start_time, 3) + # Calculate width and height + shape = ur - ll + if vertical: + shape_width = wide + shape_height = shape.y + else: + shape_width = shape.x + shape_height = wide - # Order the routes by closest to the perimeter first - # This prevents some pins near the perimeter from being blocked by other pins - ordered_pin_names = sorted(pin_names, key=lambda x: self.perimeter_dist(x)) + # Add this new pin + # They must lie on the non-preferred direction since the side supply + # pins will lie on the preferred direction + layer = self.get_layer(int(not vertical)) + nll = vector(offset.x, offset.y) + nur = vector(offset.x + shape_width, offset.y + shape_height) + rect = [nll, nur] + pin = graph_shape(name="fake", + rect=rect, + layer_name_pp=layer) + self.fake_pins.append(pin) - # Route the supply pins to the supply rails - # Route vdd first since we want it to be shorter - start_time = datetime.now() - for pin_name in ordered_pin_names: - self.route_signal(pin_name) - # if pin_name == "dout0[1]": - # self.write_debug_gds("postroute.gds", True) - print_time("Maze routing pins",datetime.now(), start_time, 3) + def get_closest_perimeter_fake_pin(self, pin): + """ Return the closest fake pin for the given pin. """ - #self.write_debug_gds("final_escape_router.gds",False) + min_dist = float("inf") + close_fake = None + for fake in self.fake_pins: + dist = pin.distance(fake) + if dist < min_dist: + min_dist = dist + close_fake = fake + return close_fake - return True - def route_signal(self, pin_name, side="all"): + def get_route_pairs(self, pin_names): + """ Return the pairs to be routed. """ - for detour_scale in [5 * pow(2, x) for x in range(5)]: - debug.info(1, "Escape routing {0} with scale {1}".format(pin_name, detour_scale)) + to_route = [] + for name in pin_names: + pin = next(iter(self.pins[name])) + fake = self.get_closest_perimeter_fake_pin(pin) + to_route.append((pin, fake, pin.distance(fake))) + return sorted(to_route, key=lambda x: x[2]) - # Clear everything in the routing grid. - self.rg.reinit() - # This is inefficient since it is non-incremental, but it was - # easier to debug. - self.prepare_blockages() - self.clear_blockages(pin_name) + def replace_layout_pins(self): + """ Replace the old layout pins with new ones around the perimeter. """ - # Add the single component of the pin as the source - # which unmarks it as a blockage too - self.add_source(pin_name) - - # Marks the grid cells all along the perimeter as a target - self.add_perimeter_target(side) - - # if pin_name == "dout0[3]": - # self.write_debug_gds("pre_route.gds", False) - # breakpoint() - - # Actually run the A* router - if self.run_router(detour_scale=detour_scale): - new_pin = self.get_perimeter_pin() - self.cell.replace_layout_pin(pin_name, new_pin) - return - - # if pin_name == "dout0[3]": - # self.write_debug_gds("pre_route.gds", False) - # breakpoint() - - self.write_debug_gds("debug_route.gds", True) + for name, pin in self.new_pins.items(): + pin = graph_shape(pin.name, pin.boundary, pin.lpp) + # Find the intersection of this pin on the perimeter + for fake in self.fake_pins: + edge = pin.intersection(fake) + if edge: + break + self.design.replace_layout_pin(name, edge) diff --git a/compiler/router/signal_grid.py b/compiler/router/signal_grid.py deleted file mode 100644 index 69ab2c94..00000000 --- a/compiler/router/signal_grid.py +++ /dev/null @@ -1,165 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from copy import deepcopy -from heapq import heappush,heappop -from openram import debug -from openram.base.vector3d import vector3d -from .grid import grid -from .grid_path import grid_path - - -class signal_grid(grid): - """ - Expand the two layer grid to include A* search functions for a source and target. - """ - - def __init__(self, ll, ur, track_factor): - """ Create a routing map of width x height cells and 2 in the z-axis. """ - grid.__init__(self, ll, ur, track_factor) - - def reinit(self): - """ Reinitialize everything for a new route. """ - - # Reset all the cells in the map - for p in self.map.values(): - p.reset() - - self.clear_source() - self.clear_target() - - def init_queue(self): - """ - Populate the queue with all the source pins with cost - to the target. Each item is a path of the grid cells. - We will use an A* search, so this cost must be pessimistic. - Cost so far will be the length of the path. - """ - # Counter is used to not require data comparison in Python 3.x - # Items will be returned in order they are added during cost ties - self.q = [] - self.counter = 0 - for s in self.source: - cost = self.cost_to_target(s) - debug.info(3, "Init: cost=" + str(cost) + " " + str([s])) - heappush(self.q, (cost, self.counter, grid_path([vector3d(s)]))) - self.counter += 1 - - def route(self, detour_scale): - """ - This does the A* maze routing with preferred direction routing. - This only works for 1 track wide routes! - """ - - # We set a cost bound of the HPWL for run-time. This can be - # over-ridden if the route fails due to pruning a feasible solution. - any_source_element = next(iter(self.source)) - cost_bound = detour_scale * self.cost_to_target(any_source_element) * grid.PREFERRED_COST - - # Check if something in the queue is already a source and a target! - for s in self.source: - if self.is_target(s): - return((grid_path([vector3d(s)]), 0)) - - # Put the source items into the queue - self.init_queue() - - # Keep expanding and adding to the priority queue until we are done - while len(self.q)>0: - # should we keep the path in the queue as well or just the final node? - (cost, count, curpath) = heappop(self.q) - debug.info(3, "Queue size: size=" + str(len(self.q)) + " " + str(cost)) - debug.info(4, "Expanding: cost=" + str(cost) + " " + str(curpath)) - - # expand the last element - neighbors = self.expand_dirs(curpath) - debug.info(4, "Neighbors: " + str(neighbors)) - - for n in neighbors: - # make a new copy of the path to not update the old ones - newpath = deepcopy(curpath) - # node is added to the map by the expand routine - newpath.append(n) - # check if we hit the target and are done - if self.is_target(n[0]): # This uses the [0] item because we are assuming 1-track wide - return (newpath, newpath.cost()) - else: - # current path cost + predicted cost - current_cost = newpath.cost() - target_cost = self.cost_to_target(n[0]) - predicted_cost = current_cost + target_cost - # only add the cost if it is less than our bound - if (predicted_cost < cost_bound): - if (self.map[n[0]].min_cost==-1 or predicted_cost self.ur.x: - return None - if wave[-1].y > self.ur.y: - return None - - while wave and self.is_wave_blocked(wave): - wf = grid_path(wave) - wave = wf.neighbor(direct) - # Bail out if we couldn't increment futher - if wave[0].x > self.ur.x or wave[-1].y > self.ur.y: - return None - # Return a start if it isn't blocked - if not self.is_wave_blocked(wave): - return wave - - return wave - - def is_wave_blocked(self, wave): - """ - Checks if any of the locations are blocked - """ - for v in wave: - if self.is_blocked(v): - return True - else: - return False - - def probe(self, wave, direct): - """ - Expand the wave until there is a blockage and return - the wave path. - """ - wave_path = grid_path() - while wave and not self.is_wave_blocked(wave): - if wave[0].x > self.ur.x or wave[-1].y > self.ur.y: - break - wave_path.append(wave) - wave = wave_path.neighbor(direct) - - return wave_path diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py deleted file mode 100644 index 0586485b..00000000 --- a/compiler/router/supply_grid_router.py +++ /dev/null @@ -1,394 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from datetime import datetime -from openram import debug -from openram.base.vector3d import vector3d -from openram import print_time -from .router import router -from .direction import direction -from .supply_grid import supply_grid -from . import grid_utils - - -class supply_grid_router(router): - """ - A router class to read an obstruction map from a gds and - routes a grid to connect the supply on the two layers. - """ - - def __init__(self, layers, design, bbox=None, pin_type=None): - """ - This will route on layers in design. It will get the blockages from - either the gds file name or the design itself (by saving to a gds file). - """ - start_time = datetime.now() - - # Power rail width in minimum wire widths - self.route_track_width = 1 - - router.__init__(self, layers, design, bbox=bbox, margin=margin, route_track_width=self.route_track_width) - - # The list of supply rails (grid sets) that may be routed - self.supply_rails = {} - # This is the same as above but as a sigle set for the all the rails - self.supply_rail_tracks = {} - - print_time("Init supply router", datetime.now(), start_time, 3) - - def route(self, vdd_name="vdd", gnd_name="gnd"): - """ - Add power supply rails and connect all pins to these rails. - """ - debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) - self.vdd_name = vdd_name - self.gnd_name = gnd_name - - # Clear the pins if we have previously routed - if (hasattr(self, 'rg')): - self.clear_pins() - else: - # Creat a routing grid over the entire area - # FIXME: This could be created only over the routing region, - # but this is simplest for now. - self.create_routing_grid(supply_grid) - - # Get the pin shapes - start_time = datetime.now() - self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) - print_time("Finding pins and blockages", datetime.now(), start_time, 3) - # Add the supply rails in a mesh network and connect H/V with vias - start_time = datetime.now() - # Block everything - self.prepare_blockages() - self.clear_blockages(self.gnd_name) - - - # Determine the rail locations - self.route_supply_rails(self.gnd_name, 0) - - # Block everything - self.prepare_blockages() - self.clear_blockages(self.vdd_name) - # Determine the rail locations - self.route_supply_rails(self.vdd_name, 1) - print_time("Routing supply rails", datetime.now(), start_time, 3) - - start_time = datetime.now() - self.route_simple_overlaps(vdd_name) - self.route_simple_overlaps(gnd_name) - print_time("Simple overlap routing", datetime.now(), start_time, 3) - - # Route the supply pins to the supply rails - # Route vdd first since we want it to be shorter - start_time = datetime.now() - self.route_pins_to_rails(vdd_name) - self.route_pins_to_rails(gnd_name) - print_time("Maze routing supplies", datetime.now(), start_time, 3) - # self.write_debug_gds("final.gds", False) - - # Did we route everything?? - if not self.check_all_routed(vdd_name): - return False - if not self.check_all_routed(gnd_name): - return False - - return True - - def route_simple_overlaps(self, pin_name): - """ - This checks for simple cases where a pin component already overlaps a supply rail. - It will add an enclosure to ensure the overlap in wide DRC rule cases. - """ - debug.info(1, "Routing simple overlap pins for {0}".format(pin_name)) - - # These are the wire tracks - wire_tracks = self.supply_rail_tracks[pin_name] - routed_count=0 - for pg in self.pin_groups[pin_name]: - if pg.is_routed(): - continue - - # First, check if we just overlap, if so, we are done. - overlap_grids = wire_tracks & pg.grids - if len(overlap_grids)>0: - routed_count += 1 - pg.set_routed() - continue - - # Else, if we overlap some of the space track, we can patch it with an enclosure - # pg.create_simple_overlap_enclosure(pg.grids) - # pg.add_enclosure(self.cell) - - debug.info(1, "Routed {} simple overlap pins".format(routed_count)) - - def finalize_supply_rails(self, name): - """ - Determine which supply rails overlap and can accomodate a via. - Remove any supply rails that do not have a via since they are disconnected. - NOTE: It is still possible though unlikely that there are disconnected groups of rails. - """ - - all_rails = self.supply_rails[name] - - connections = set() - via_areas = [] - for i1, r1 in enumerate(all_rails): - # Only consider r1 horizontal rails - e = next(iter(r1)) - if e.z==1: - continue - - # We need to move this rail to the other layer for the z indices to match - # during the intersection. This also makes a copy. - new_r1 = {vector3d(i.x, i.y, 1) for i in r1} - - for i2, r2 in enumerate(all_rails): - # Never compare to yourself - if i1==i2: - continue - - # Only consider r2 vertical rails - e = next(iter(r2)) - if e.z==0: - continue - - # Determine if we have sufficient overlap and, if so, - # remember: - # the indices to determine a rail is connected to another - # the overlap area for placement of a via - overlap = new_r1 & r2 - if len(overlap) >= 1: - debug.info(3, "Via overlap {0} {1}".format(len(overlap),overlap)) - connections.update([i1, i2]) - via_areas.append(overlap) - - # Go through and add the vias at the center of the intersection - for area in via_areas: - ll = grid_utils.get_lower_left(area) - ur = grid_utils.get_upper_right(area) - center = (ll + ur).scale(0.5, 0.5, 0) - self.add_via(center, 1) - - # Determien which indices were not connected to anything above - missing_indices = set([x for x in range(len(self.supply_rails[name]))]) - missing_indices.difference_update(connections) - - # Go through and remove those disconnected indices - # (No via was added, so that doesn't need to be removed) - for rail_index in sorted(missing_indices, reverse=True): - ll = grid_utils.get_lower_left(all_rails[rail_index]) - ur = grid_utils.get_upper_right(all_rails[rail_index]) - debug.info(1, "Removing disconnected supply rail {0} .. {1}".format(ll, ur)) - self.supply_rails[name].pop(rail_index) - - # Make the supply rails into a big giant set of grids for easy blockages. - # Must be done after we determine which ones are connected. - self.create_supply_track_set(name) - - def add_supply_rails(self, name): - """ - Add the shapes that represent the routed supply rails. - This is after the paths have been pruned and only include rails that are - connected with vias. - """ - for rail in self.supply_rails[name]: - ll = grid_utils.get_lower_left(rail) - ur = grid_utils.get_upper_right(rail) - z = ll.z - pin = self.compute_pin_enclosure(ll, ur, z, name) - debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin)) - self.cell.add_layout_pin(text=name, - layer=pin.layer, - offset=pin.ll(), - width=pin.width(), - height=pin.height()) - - def compute_supply_rails(self, name, supply_number): - """ - Compute the unblocked locations for the horizontal and vertical supply rails. - Go in a raster order from bottom to the top (for horizontal) and left to right - (for vertical). Start with an initial start_offset in x and y direction. - """ - - self.supply_rails[name]=[] - - max_yoffset = self.rg.ur.y - max_xoffset = self.rg.ur.x - min_yoffset = self.rg.ll.y - min_xoffset = self.rg.ll.x - - # Horizontal supply rails - start_offset = min_yoffset + supply_number - for offset in range(start_offset, max_yoffset, 2): - # Seed the function at the location with the given width - wave = [vector3d(min_xoffset, offset, 0)] - # While we can keep expanding east in this horizontal track - while wave and wave[0].x < max_xoffset: - added_rail = self.find_supply_rail(name, wave, direction.EAST) - if not added_rail: - # Just seed with the next one - wave = [x+vector3d(1, 0, 0) for x in wave] - else: - # Seed with the neighbor of the end of the last rail - wave = added_rail.neighbor(direction.EAST) - - # Vertical supply rails - start_offset = min_xoffset + supply_number - for offset in range(start_offset, max_xoffset, 2): - # Seed the function at the location with the given width - wave = [vector3d(offset, min_yoffset, 1)] - # While we can keep expanding north in this vertical track - while wave and wave[0].y < max_yoffset: - added_rail = self.find_supply_rail(name, wave, direction.NORTH) - if not added_rail: - # Just seed with the next one - wave = [x + vector3d(0, 1, 0) for x in wave] - else: - # Seed with the neighbor of the end of the last rail - wave = added_rail.neighbor(direction.NORTH) - - def find_supply_rail(self, name, seed_wave, direct): - """ - Find a start location, probe in the direction, and see if the rail is big enough - to contain a via, and, if so, add it. - """ - # Sweep to find an initial unblocked valid wave - start_wave = self.rg.find_start_wave(seed_wave, direct) - - # This means there were no more unblocked grids in the row/col - if not start_wave: - return None - - wave_path = self.probe_supply_rail(name, start_wave, direct) - - self.approve_supply_rail(name, wave_path) - - # Return the rail whether we approved it or not, - # as it will be used to find the next start location - return wave_path - - def probe_supply_rail(self, name, start_wave, direct): - """ - This finds the first valid starting location and routes a supply rail - in the given direction. - It returns the space after the end of the rail to seed another call for multiple - supply rails in the same "track" when there is a blockage. - """ - - # Expand the wave to the right - wave_path = self.rg.probe(start_wave, direct) - - if not wave_path: - return None - - # drop the first and last steps to leave escape routing room - # around the blockage that stopped the probe - # except, don't drop the first if it is the first in a row/column - if (direct==direction.NORTH and start_wave[0].y>0): - wave_path.trim_first() - elif (direct == direction.EAST and start_wave[0].x>0): - wave_path.trim_first() - - wave_path.trim_last() - - return wave_path - - def approve_supply_rail(self, name, wave_path): - """ - Check if the supply rail is sufficient (big enough) and add it to the - data structure. Return whether it was added or not. - """ - # We must have at least 2 tracks to drop plus 2 tracks for a via - if len(wave_path) >= 4 * self.route_track_width: - grid_set = wave_path.get_grids() - self.supply_rails[name].append(grid_set) - return True - - return False - - def route_supply_rails(self, name, supply_number): - """ - Route the horizontal and vertical supply rails across the entire design. - Must be done with lower left at 0,0 - """ - debug.info(1, "Routing supply rail {0}.".format(name)) - - # Compute the grid locations of the supply rails - self.compute_supply_rails(name, supply_number) - - # Add the supply rail vias (and prune disconnected rails) - self.finalize_supply_rails(name) - - # Add the rails themselves - self.add_supply_rails(name) - - def create_supply_track_set(self, pin_name): - """ - Make a single set of all the tracks for the rail and wire itself. - """ - rail_set = set() - for rail in self.supply_rails[pin_name]: - rail_set.update(rail) - self.supply_rail_tracks[pin_name] = rail_set - - def route_pins_to_rails(self, pin_name): - """ - This will route each of the remaining pin components to the supply rails. - After it is done, the cells are added to the pin blockage list. - """ - - remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) - debug.info(1, "Maze routing {0} with {1} pin components to connect.".format(pin_name, - remaining_components)) - - for index, pg in enumerate(self.pin_groups[pin_name]): - if pg.is_routed(): - continue - - debug.info(3, "Routing component {0} {1}".format(pin_name, index)) - - # Clear everything in the routing grid. - self.rg.reinit() - - # This is inefficient since it is non-incremental, but it was - # easier to debug. - self.prepare_blockages() - self.clear_blockages(self.vdd_name) - - # Add the single component of the pin as the source - # which unmarks it as a blockage too - self.add_pin_component_source(pin_name, index) - - # Add all of the rails as targets - # Don't add the other pins, but we could? - self.add_supply_rail_target(pin_name) - - # Actually run the A* router - if not self.run_router(detour_scale=5): - self.write_debug_gds("debug_route.gds") - - # if index==3 and pin_name=="vdd": - # self.write_debug_gds("route.gds",False) - - def add_supply_rail_target(self, pin_name): - """ - Add the supply rails of given name as a routing target. - """ - debug.info(4, "Add supply rail target {}".format(pin_name)) - # Add the wire itself as the target - self.rg.set_target(self.supply_rail_tracks[pin_name]) - # But unblock all the rail tracks including the space - self.rg.set_blocked(self.supply_rail_tracks[pin_name], False) - - def set_supply_rail_blocked(self, value=True): - """ - Add the supply rails of given name as a routing target. - """ - debug.info(4, "Blocking supply rail") - for rail_name in self.supply_rail_tracks: - self.rg.set_blocked(self.supply_rail_tracks[rail_name]) diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py new file mode 100644 index 00000000..f6acd24c --- /dev/null +++ b/compiler/router/supply_router.py @@ -0,0 +1,269 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from openram import debug +from openram.base.vector import vector +from openram import OPTS +from .graph import graph +from .graph_shape import graph_shape +from .router import router + + +class supply_router(router): + """ + This is the supply router that uses the Hanan grid graph method. + """ + + def __init__(self, layers, design, bbox=None, pin_type=None): + + # `router` is the base router class + router.__init__(self, layers, design, bbox) + + # Side supply pin type + # (can be "top", "bottom", "right", "left", and "ring") + self.pin_type = pin_type + # New pins are the side supply pins + self.new_pins = {} + + + def route(self, vdd_name="vdd", gnd_name="gnd"): + """ Route the given pins in the given order. """ + debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name)) + + # Save pin names + self.vdd_name = vdd_name + self.gnd_name = gnd_name + + # Prepare gdsMill to find pins and blockages + self.prepare_gds_reader() + + # Find pins to be routed + self.find_pins(vdd_name) + self.find_pins(gnd_name) + + # Find blockages and vias + self.find_blockages() + self.find_vias() + + # Convert blockages and vias if they overlap a pin + self.convert_vias() + self.convert_blockages() + + # Add side pins + if self.pin_type in ["top", "bottom", "right", "left"]: + self.add_side_pin(vdd_name) + self.add_side_pin(gnd_name) + elif self.pin_type == "ring": + self.add_ring_pin(vdd_name) + self.add_ring_pin(gnd_name) + else: + debug.warning("Side supply pins aren't created.") + + # Add vdd and gnd pins as blockages as well + # NOTE: This is done to make vdd and gnd pins DRC-safe + for pin in self.all_pins: + self.blockages.append(self.inflate_shape(pin)) + + # Route vdd and gnd + for pin_name in [vdd_name, gnd_name]: + pins = self.pins[pin_name] + # Route closest pins according to the minimum spanning tree + for source, target in self.get_mst_pairs(list(pins)): + # This is the routing region scale + scale = 1 + while True: + # Create the graph + g = graph(self) + region = g.create_graph(source, target, scale) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If there is no path found, exponentially try again with a + # larger routing region + if path is None: + rll, rur = region + bll, bur = self.bbox + # Stop scaling the region and throw an error + if rll.x < bll.x and rll.y < bll.y and \ + rur.x > bur.x and rur.y > bur.y: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Exponentially scale the region + scale *= 2 + debug.info(0, "Retry routing in larger routing region with scale {}".format(scale)) + continue + # Create the path shapes on layout + self.add_path(path) + # Find the recently added shapes + self.prepare_gds_reader() + self.find_blockages(pin_name) + self.find_vias() + break + + + def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4): + """ Add supply pin to one side of the layout. """ + + ll, ur = self.bbox + vertical = side in ["left", "right"] + inner = pin_name == self.gnd_name + + # Calculate wires' wideness + wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1) + + # Calculate the offset for the inner ring + if inner: + margin = wideness * 2 + else: + margin = 0 + + # Calculate the lower left coordinate + if side == "top": + offset = vector(ll.x + margin, ur.y - wideness - margin) + elif side == "bottom": + offset = vector(ll.x + margin, ll.y + margin) + elif side == "left": + offset = vector(ll.x + margin, ll.y + margin) + elif side == "right": + offset = vector(ur.x - wideness - margin, ll.y + margin) + + # Calculate width and height + shape = ur - ll + if vertical: + shape_width = wideness + shape_height = shape.y + else: + shape_width = shape.x + shape_height = wideness + if inner: + if vertical: + shape_height -= margin * 2 + else: + shape_width -= margin * 2 + + # Add this new pin + layer = self.get_layer(int(vertical)) + pin = self.design.add_layout_pin(text=pin_name, + layer=layer, + offset=offset, + width=shape_width, + height=shape_height) + + # Add fake pins on this new pin evenly + fake_pins = [] + if vertical: + space = (shape_height - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1) + start_offset = vector(offset.x, offset.y + wideness) + else: + space = (shape_width - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1) + start_offset = vector(offset.x + wideness, offset.y) + for i in range(1, num_fake_pins + 1): + if vertical: + offset = vector(start_offset.x, start_offset.y + i * (space + self.track_wire)) + ll = vector(offset.x, offset.y - self.track_wire) + ur = vector(offset.x + wideness, offset.y) + else: + offset = vector(start_offset.x + i * (space + self.track_wire), start_offset.y) + ll = vector(offset.x - self.track_wire, offset.y) + ur = vector(offset.x, offset.y + wideness) + rect = [ll, ur] + fake_pin = graph_shape(name=pin_name, + rect=rect, + layer_name_pp=layer) + fake_pins.append(fake_pin) + return pin, fake_pins + + + def add_ring_pin(self, pin_name, num_vias=3, num_fake_pins=4): + """ Add the supply ring to the layout. """ + + # Add side pins + new_pins = [] + for side in ["top", "bottom", "right", "left"]: + new_shape, fake_pins = self.add_side_pin(pin_name, side, num_vias, num_fake_pins) + ll, ur = new_shape.rect + rect = [ll, ur] + layer = self.get_layer(side in ["left", "right"]) + new_pin = graph_shape(name=pin_name, + rect=rect, + layer_name_pp=layer) + new_pins.append(new_pin) + self.pins[pin_name].update(fake_pins) + self.fake_pins.extend(fake_pins) + + # Add vias to the corners + shift = self.track_wire + self.track_space + half_wide = self.track_wire / 2 + for i in range(4): + ll, ur = new_pins[i].rect + if i % 2: + top_left = vector(ur.x - (num_vias - 1) * shift - half_wide, ll.y + (num_vias - 1) * shift + half_wide) + else: + top_left = vector(ll.x + half_wide, ur.y - half_wide) + for j in range(num_vias): + for k in range(num_vias): + offset = vector(top_left.x + j * shift, top_left.y - k * shift) + self.design.add_via_center(layers=self.layers, + offset=offset) + + # Save side pins for routing + self.new_pins[pin_name] = new_pins + for pin in new_pins: + self.blockages.append(self.inflate_shape(pin)) + + + def get_mst_pairs(self, pins): + """ + Return the pin pairs from the minimum spanning tree in a graph that + connects all pins together. + """ + + pin_count = len(pins) + + # Create an adjacency matrix that connects all pins + edges = [[0] * pin_count for i in range(pin_count)] + for i in range(pin_count): + for j in range(pin_count): + # Skip if they're the same pin + if i == j: + continue + # Skip if both pins are fake + if pins[i] in self.fake_pins and pins[j] in self.fake_pins: + continue + edges[i][j] = pins[i].distance(pins[j]) + + pin_connected = [False] * pin_count + pin_connected[0] = True + + # Add the minimum cost edge in each iteration (Prim's) + mst_pairs = [] + for i in range(pin_count - 1): + min_cost = float("inf") + s = 0 + t = 0 + # Iterate over already connected pins + for m in range(pin_count): + # Skip if not connected + if not pin_connected[m]: + continue + # Iterate over this pin's neighbors + for n in range(pin_count): + # Skip if already connected or isn't a neighbor + if pin_connected[n] or edges[m][n] == 0: + continue + # Choose this edge if it's better the the current one + if edges[m][n] < min_cost: + min_cost = edges[m][n] + s = m + t = n + pin_connected[t] = True + mst_pairs.append((pins[s], pins[t])) + + return mst_pairs + + + def get_new_pins(self, name): + """ Return the new supply pins added by this router. """ + + return self.new_pins[name] diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py deleted file mode 100644 index 77247152..00000000 --- a/compiler/router/supply_tree_router.py +++ /dev/null @@ -1,208 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2023 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from datetime import datetime -from scipy.sparse import csr_matrix -from scipy.sparse.csgraph import minimum_spanning_tree -from openram import debug -from openram import print_time -from .router import router -from . import grid_utils -from .signal_grid import signal_grid - - -class supply_tree_router(router): - """ - A router class to read an obstruction map from a gds and - routes a grid to connect the supply on the two layers. - """ - - def __init__(self, layers, design, bbox=None, pin_type=None): - """ - This will route on layers in design. It will get the blockages from - either the gds file name or the design itself (by saving to a gds file). - """ - # Power rail width in minimum wire widths - # This is set to match the signal router so that the grids are aligned - # for prettier routes. - self.route_track_width = 1 - - # The pin escape router already made the bounding box big enough, - # so we can use the regular bbox here. - if pin_type: - debug.check(pin_type in ["left", "right", "top", "bottom", "single", "ring"], - "Invalid pin type {}".format(pin_type)) - self.pin_type = pin_type - router.__init__(self, - layers, - design, - bbox=bbox, - route_track_width=self.route_track_width) - - - def route(self, vdd_name="vdd", gnd_name="gnd"): - """ - Route the two nets in a single layer. - Setting pin stripe will make a power rail on the left side. - """ - debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) - self.vdd_name = vdd_name - self.gnd_name = gnd_name - - # Clear the pins if we have previously routed - if (hasattr(self, 'rg')): - self.clear_pins() - else: - # Creat a routing grid over the entire area - # FIXME: This could be created only over the routing region, - # but this is simplest for now. - self.create_routing_grid(signal_grid) - - start_time = datetime.now() - - # Get the pin shapes - self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) - print_time("Finding pins and blockages", datetime.now(), start_time, 3) - - # Add side pins if enabled - if self.pin_type in ["left", "right", "top", "bottom"]: - self.add_side_supply_pin(self.vdd_name, side=self.pin_type) - self.add_side_supply_pin(self.gnd_name, side=self.pin_type) - elif self.pin_type == "ring": - self.add_ring_supply_pin(self.vdd_name) - self.add_ring_supply_pin(self.gnd_name) - - #self.write_debug_gds("initial_tree_router.gds",False) - #breakpoint() - - # Route the supply pins to the supply rails - # Route vdd first since we want it to be shorter - start_time = datetime.now() - self.route_pins(vdd_name) - self.route_pins(gnd_name) - print_time("Maze routing supplies", datetime.now(), start_time, 3) - - # Did we route everything?? - if not self.check_all_routed(vdd_name): - return False - if not self.check_all_routed(gnd_name): - return False - - return True - - def route_pins(self, pin_name): - """ - This will route each of the remaining pin components to the other pins. - After it is done, the cells are added to the pin blockage list. - """ - - remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) - debug.info(1, "Routing {0} with {1} pins.".format(pin_name, - remaining_components)) - - # Save pin center locations - if False: - debug.info(2, "Creating location file {0}_{1}.csv".format(self.cell.name, pin_name)) - f = open("{0}_{1}.csv".format(self.cell.name, pin_name), "w") - pin_size = len(self.pin_groups[pin_name]) - for index1, pg1 in enumerate(self.pin_groups[pin_name]): - location = list(pg1.grids)[0] - f.write("{0},{1},{2}\n".format(location.x, location.y, location.z)) - f.close() - - # Create full graph - debug.info(2, "Creating adjacency matrix") - pin_size = len(self.pin_groups[pin_name]) - adj_matrix = [[0] * pin_size for i in range(pin_size)] - - for index1, pg1 in enumerate(self.pin_groups[pin_name]): - for index2, pg2 in enumerate(self.pin_groups[pin_name]): - if index1>=index2: - continue - dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids)) - adj_matrix[index1][index2] = dist - - # Find MST - debug.info(2, "Finding Minimum Spanning Tree") - X = csr_matrix(adj_matrix) - from scipy.sparse import save_npz - #print("Saving {}.npz".format(self.cell.name)) - #save_npz("{}.npz".format(self.cell.name), X) - #exit(1) - - Tcsr = minimum_spanning_tree(X) - mst = Tcsr.toarray().astype(int) - connections = [] - for x in range(pin_size): - for y in range(pin_size): - if x >= y: - continue - if mst[x][y]>0: - connections.append((x, y)) - - # Route MST components - level=99 - for index, (src, dest) in enumerate(connections): - if not (index % 25): - debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index)) - self.route_signal(pin_name, src, dest) - if False and pin_name == "gnd": - debug.info(level, "\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages)) - debug.info(level, ("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))) - self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False) - - #self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False) - #return - - def route_signal(self, pin_name, src_idx, dest_idx): - - # First pass, try to route normally - # Second pass, clear prior pin blockages so that you can route over other metal - # of the same supply. Otherwise, this can create a lot of circular routes due to accidental overlaps. - for unblock_routes in [False, True]: - for detour_scale in [2 * pow(2, x) for x in range(5)]: - debug.info(2, "Routing {0} to {1} with scale {2}".format(src_idx, dest_idx, detour_scale)) - - # Clear everything in the routing grid. - self.rg.reinit() - - # This is inefficient since it is non-incremental, but it was - # easier to debug. - self.prepare_blockages(src=(pin_name, src_idx), dest=(pin_name, dest_idx)) - if unblock_routes: - msg = "Unblocking supply self blockages to improve access (may cause DRC errors):\n{0}\n{1})" - debug.warning(msg.format(pin_name, - self.pin_groups[pin_name][src_idx].pins)) - self.set_blockages(self.path_blockages, False) - - # Add the single component of the pin as the source - # which unmarks it as a blockage too - self.set_pin_component_source(pin_name, src_idx) - - # Marks all pin components except index as target - # which unmarks it as a blockage too - self.set_pin_component_target(pin_name, dest_idx) - - # Actually run the A* router - if self.run_router(detour_scale=detour_scale): - return - #if detour_scale > 2: - # self.write_debug_gds("route_{0}_{1}_d{2}.gds".format(src_idx, dest_idx, detour_scale), False) - - self.write_debug_gds("debug_route.gds", True) - - def add_io_pin(self, instance, pin_name, new_name=""): - """ - Add a signle input or output pin up to metal 3. - """ - pin = instance.get_pins(pin_name) - - if new_name == "": - new_name = pin_name - - # Just use the power pin function for now to save code - self.add_power_pin(name=new_name, loc=pin.center(), start_layer=pin.layer) diff --git a/compiler/sram.py b/compiler/sram.py index 7b807d66..cc269d8f 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -60,6 +60,14 @@ class sram(): if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) + def get_sp_name(self): + if OPTS.use_pex: + # Use the extracted spice file + return self.pex_name + else: + # Use generated spice file for characterization + return self.sp_name + def sp_write(self, name, lvs=False, trim=False): self.s.sp_write(name, lvs, trim) @@ -95,18 +103,40 @@ class sram(): # is loaded and the right tools are selected from openram import verify from openram.characterizer import functional + from openram.characterizer import delay # Save the spice file start_time = datetime.datetime.now() spname = OPTS.output_path + self.s.name + ".sp" debug.print_raw("SP: Writing to {0}".format(spname)) self.sp_write(spname) + + # Save a functional simulation file with default period functional(self.s, - os.path.basename(spname), + spname, cycles=200, output_path=OPTS.output_path) print_time("Spice writing", datetime.datetime.now(), start_time) + # Save stimulus and measurement file + start_time = datetime.datetime.now() + debug.print_raw("DELAY: Writing stimulus...") + d = delay(self.s, spname, ("TT", 5, 25), output_path=OPTS.output_path) + if (self.s.num_spare_rows == 0): + probe_address = "1" * self.s.addr_size + else: + probe_address = "0" + "1" * (self.s.addr_size - 1) + probe_data = self.s.word_size - 1 + d.analysis_init(probe_address, probe_data) + d.targ_read_ports.extend(self.s.read_ports) + d.targ_write_ports = [self.s.write_ports[0]] + d.write_delay_stimulus() + print_time("DELAY", datetime.datetime.now(), start_time) + + # Save trimmed spice file + temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path) + self.sp_write(temp_trim_sp, lvs=False, trim=True) + if not OPTS.netlist_only: # Write the layout start_time = datetime.datetime.now() @@ -154,8 +184,6 @@ class sram(): # Use generated spice file for characterization sp_file = spname - # Save a functional simulation file - # Characterize the design start_time = datetime.datetime.now() from openram.characterizer import lib diff --git a/compiler/sram_config.py b/compiler/sram_config.py index 465d6434..afb660f0 100644 --- a/compiler/sram_config.py +++ b/compiler/sram_config.py @@ -14,7 +14,8 @@ from openram import OPTS class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): + def __init__(self, word_size, num_words, write_size=None, num_banks=1, + words_per_row=None, num_spare_rows=0, num_spare_cols=0): self.word_size = word_size self.num_words = num_words # Don't add a write mask if it is the same size as the data word @@ -38,6 +39,11 @@ class sram_config: except ImportError: self.array_col_multiple = 1 + if not self.num_spare_cols: + self.num_spare_cols = 0 + + if not self.num_spare_rows: + self.num_spare_rows = 0 # This will get over-written when we determine the organization self.words_per_row = words_per_row @@ -175,3 +181,43 @@ class sram_config: return int(words_per_row * tentative_num_rows / 16) return words_per_row + + def setup_multiport_constants(self): + """ + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are the write-only port indices. + self.writeonly_ports = [] + # These are the read/write and read-only port indices + self.read_ports = [] + # These are the read-only port indices. + self.readonly_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) + + # The order is always fixed as RW, W, R + port_number = 0 + for port in range(OPTS.num_rw_ports): + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_w_ports): + self.write_ports.append(port_number) + self.writeonly_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_r_ports): + self.read_ports.append(port_number) + self.readonly_ports.append(port_number) + port_number += 1 diff --git a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py index 704204dd..71d4a3eb 100755 --- a/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1rw_1r_spare_cols_test.py @@ -23,9 +23,6 @@ class sram_1bank_2mux_1rw_1r_spare_cols_test(openram_test): openram.init_openram(config_file, is_unit_test=True) from openram import sram_config - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 diff --git a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py index ae5ee11b..ce3fef2a 100755 --- a/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_1w_1r_spare_cols_test.py @@ -23,9 +23,6 @@ class sram_1bank_2mux_1w_1r_spare_cols_test(openram_test): openram.init_openram(config_file, is_unit_test=True) from openram import sram_config - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - OPTS.num_rw_ports = 0 OPTS.num_w_ports = 1 OPTS.num_r_ports = 1 diff --git a/compiler/tests/20_sram_1bank_2mux_global_test.py b/compiler/tests/20_sram_1bank_2mux_global_test.py index f8b73aca..01d831ee 100755 --- a/compiler/tests/20_sram_1bank_2mux_global_test.py +++ b/compiler/tests/20_sram_1bank_2mux_global_test.py @@ -31,9 +31,6 @@ class sram_1bank_2mux_global_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - c = sram_config(word_size=8, num_words=32, num_banks=1, diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py index 419390a5..0f8a1d7f 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_spare_cols_test.py @@ -30,9 +30,6 @@ class sram_1bank_2mux_wmask_spare_cols_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - c = sram_config(word_size=8, write_size=4, num_words=64, diff --git a/compiler/tests/20_sram_1bank_2mux_wmask_test.py b/compiler/tests/20_sram_1bank_2mux_wmask_test.py index 05d89040..cc3c2322 100755 --- a/compiler/tests/20_sram_1bank_2mux_wmask_test.py +++ b/compiler/tests/20_sram_1bank_2mux_wmask_test.py @@ -30,9 +30,6 @@ class sram_1bank_2mux_wmask_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - c = sram_config(word_size=8, write_size=4, num_words=64, diff --git a/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py index 1ab0d745..ffe7a673 100755 --- a/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_4mux_1rw_1r_test.py @@ -23,9 +23,6 @@ class sram_1bank_4mux_1rw_1r_test(openram_test): openram.init_openram(config_file, is_unit_test=True) from openram import sram_config - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 diff --git a/compiler/tests/20_sram_1bank_4mux_test.py b/compiler/tests/20_sram_1bank_4mux_test.py index 15f9f31d..3f1f0886 100755 --- a/compiler/tests/20_sram_1bank_4mux_test.py +++ b/compiler/tests/20_sram_1bank_4mux_test.py @@ -30,9 +30,6 @@ class sram_1bank_4mux_test(openram_test): num_spare_rows = 0 num_spare_cols = 0 - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - c = sram_config(word_size=4, num_words=64, num_banks=1, diff --git a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py index 7ce6ca8d..c95fec84 100755 --- a/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py +++ b/compiler/tests/20_sram_1bank_8mux_1rw_1r_test.py @@ -23,9 +23,6 @@ class sram_1bank_8mux_1rw_1r_test(openram_test): openram.init_openram(config_file, is_unit_test=True) from openram import sram_config - if OPTS.tech_name == "freepdk45": - OPTS.route_supplies = False - OPTS.num_rw_ports = 1 OPTS.num_r_ports = 1 OPTS.num_w_ports = 0 diff --git a/compiler/tests/21_model_delay_test.py b/compiler/tests/21_model_delay_test.py index 681f4fff..46e2e66f 100755 --- a/compiler/tests/21_model_delay_test.py +++ b/compiler/tests/21_model_delay_test.py @@ -25,6 +25,8 @@ class model_delay_test(openram_test): openram.init_openram(config_file, is_unit_test=True) OPTS.analytical_delay = False OPTS.netlist_only = True + OPTS.spice_name = "Xyce" + OPTS.num_sim_threads = 8 # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload diff --git a/compiler/tests/21_ngspice_delay_extra_rows_test.py b/compiler/tests/21_ngspice_delay_extra_rows_test.py index bf2dc358..531816f5 100755 --- a/compiler/tests/21_ngspice_delay_extra_rows_test.py +++ b/compiler/tests/21_ngspice_delay_extra_rows_test.py @@ -61,35 +61,36 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'slew_lh': [0.2592187], - 'slew_hl': [0.2592187], - 'delay_lh': [0.2465583], - 'disabled_write0_power': [0.1924678], - 'disabled_read0_power': [0.152483], - 'write0_power': [0.3409064], - 'disabled_read1_power': [0.1737818], - 'read0_power': [0.3096708], - 'read1_power': [0.3107916], - 'delay_hl': [0.2465583], - 'write1_power': [0.26915849999999997], - 'leakage_power': 0.002044307, - 'min_period': 0.898, - 'disabled_write1_power': [0.201411]} + golden_data = {'delay_hl': [0.263898], + 'delay_lh': [0.263898], + 'disabled_read0_power': [0.06625703], + 'disabled_read1_power': [0.07531121], + 'disabled_write0_power': [0.09350641999999999], + 'disabled_write1_power': [0.09988823000000001], + 'leakage_power': 0.01192385, + 'min_period': 2.031, + 'read0_power': [0.14745439999999999], + 'read1_power': [0.1470831], + 'slew_hl': [0.027165], + 'slew_lh': [0.027165], + 'write0_power': [0.1630546], + 'write1_power': [0.1319501]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'read1_power': [12.11658], - 'write1_power': [10.52653], - 'read0_power': [11.956710000000001], - 'disabled_write0_power': [7.673665], - 'disabled_write1_power': [7.981922000000001], - 'slew_lh': [1.868836], - 'slew_hl': [1.868836], - 'delay_hl': [1.8598510000000001], - 'delay_lh': [1.8598510000000001], - 'leakage_power': 0.005457728, - 'disabled_read0_power': [5.904712], - 'min_period': 6.875, - 'disabled_read1_power': [7.132159], - 'write0_power': [13.406400000000001]} + golden_data = {'delay_hl': [1.8259260000000002], + 'delay_lh': [1.8259260000000002], + 'disabled_read0_power': [6.722809], + 'disabled_read1_power': [8.104113], + 'disabled_write0_power': [8.900671], + 'disabled_write1_power': [9.188668], + 'leakage_power': 0.6977637, + 'min_period': 6.562, + 'read0_power': [15.45948], + 'read1_power': [15.48587], + 'slew_hl': [0.1936536], + 'slew_lh': [0.1936536], + 'write0_power': [17.03442], + 'write1_power': [13.05424]} + else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 8f3e05be..eaf5d8a2 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -69,35 +69,36 @@ class timing_sram_test(openram_test): data.update(port_data[0]) if OPTS.tech_name == "freepdk45": - golden_data = {'delay_hl': [0.24671600000000002], - 'delay_lh': [0.24671600000000002], - 'disabled_read0_power': [0.1749204], - 'disabled_read1_power': [0.1873704], - 'disabled_write0_power': [0.204619], - 'disabled_write1_power': [0.2262653], - 'leakage_power': 0.0021375310000000002, - 'min_period': 0.977, - 'read0_power': [0.3856875], - 'read1_power': [0.38856060000000003], - 'slew_hl': [0.2842019], - 'slew_lh': [0.2842019], - 'write0_power': [0.45274410000000004], - 'write1_power': [0.38727789999999995]} + golden_data = {'delay_hl': [0.2764415], + 'delay_lh': [0.2764415], + 'disabled_read0_power': [0.18364834], + 'disabled_read1_power': [0.20878333999999998], + 'disabled_write0_power': [0.24064433999999998], + 'disabled_write1_power': [0.27207664], + 'leakage_power': 0.0443369, + 'min_period': 0.938, + 'read0_power': [0.37790994], + 'read1_power': [0.37646214], + 'slew_hl': [0.0266144], + 'slew_lh': [0.0266144], + 'write0_power': [0.44694044], + 'write1_power': [0.36824544000000003]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.882508], - 'delay_lh': [1.882508], - 'disabled_read0_power': [7.487227], - 'disabled_read1_power': [8.749013], - 'disabled_write0_power': [9.268901], - 'disabled_write1_power': [9.962973], - 'leakage_power': 0.0046686359999999994, - 'min_period': 7.188, - 'read0_power': [16.64011], - 'read1_power': [17.20825], - 'slew_hl': [2.039655], - 'slew_lh': [2.039655], - 'write0_power': [19.31883], - 'write1_power': [15.297369999999999]} + golden_data = {'delay_hl': [1.905376], + 'delay_lh': [1.905376], + 'disabled_read0_power': [7.673850999999999], + 'disabled_read1_power': [10.051073], + 'disabled_write0_power': [10.638803], + 'disabled_write1_power': [10.385253], + 'leakage_power': 2.704021, + 'min_period': 6.875, + 'read0_power': [17.583853], + 'read1_power': [17.689162999999997], + 'slew_hl': [0.19331199999999998], + 'slew_lh': [0.19331199999999998], + 'write0_power': [20.607043], + 'write1_power': [16.107403]} + else: self.assertTrue(False) # other techs fail diff --git a/compiler/tests/21_xyce_delay_test.py b/compiler/tests/21_xyce_delay_test.py index be5a70ee..1c62ad1b 100755 --- a/compiler/tests/21_xyce_delay_test.py +++ b/compiler/tests/21_xyce_delay_test.py @@ -84,20 +84,20 @@ class timing_sram_test(openram_test): 'write0_power': [0.429895901], 'write1_power': [0.383337501]} elif OPTS.tech_name == "scn4m_subm": - golden_data = {'delay_hl': [1.884186], - 'delay_lh': [1.884186], - 'disabled_read0_power': [20.86336], - 'disabled_read1_power': [22.10636], - 'disabled_write0_power': [22.62321], - 'disabled_write1_power': [23.316010000000002], - 'leakage_power': 13.351170000000002, - 'min_period': 7.188, - 'read0_power': [29.90159], - 'read1_power': [30.47858], - 'slew_hl': [2.042723], - 'slew_lh': [2.042723], - 'write0_power': [32.13199], - 'write1_power': [28.46703]} + golden_data = {'delay_hl': [1.78586], + 'delay_lh': [1.78586], + 'disabled_read0_power': [7.8296693788], + 'disabled_read1_power': [9.1464723788], + 'disabled_write0_power': [9.6889073788], + 'disabled_write1_power': [10.4123023788], + 'leakage_power': 0.0002442851, + 'min_period': 6.875, + 'read0_power': [16.995952378800002], + 'read1_power': [17.5845523788], + 'slew_hl': [2.039202], + 'slew_lh': [2.039202], + 'write0_power': [19.785462378800002], + 'write1_power': [15.742192378799999]} else: self.assertTrue(False) # other techs fail # Check if no too many or too few results diff --git a/compiler/tests/30_openram_sram_char_test.py b/compiler/tests/30_openram_sram_char_test.py new file mode 100755 index 00000000..748abecf --- /dev/null +++ b/compiler/tests/30_openram_sram_char_test.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os, re +import shutil +import getpass +import unittest +from testutils import * + +import openram +from openram import debug +from openram import OPTS + + +class sram_char_test(openram_test): + + def runTest(self): + global OPTS + out_file = "sram_2_16_1_{0}".format(OPTS.tech_name) + out_path = "{0}/testsram_{1}_{2}_{3}".format(OPTS.openram_temp, OPTS.tech_name, getpass.getuser(), os.getpid()) + OPTS.output_name = out_file + OPTS.output_path = out_path + + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + config_file = "{}/tests/configs/config_mem_char_func".format(OPENRAM_HOME) + + openram.init_openram(config_file, is_unit_test=False) + sp_file = "{0}/tests/sp_files/{1}.sp".format(OPENRAM_HOME, OPTS.output_name) + + debug.info(1, "Testing commandline characterizer script sram_char.py with 2-bit, 16 word SRAM.") + + # make sure we start without the files existing + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + try: + os.makedirs(out_path, 0o0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(out_path, 0o0750) + + # specify the same verbosity for the system call + options = "" + for i in range(OPTS.verbose_level): + options += " -v" + + if OPTS.spice_name: + options += " -s {}".format(OPTS.spice_name) + + if OPTS.tech_name: + options += " -t {}".format(OPTS.tech_name) + + options += " -j 2" + + # Always perform code coverage + if OPTS.coverage == 0: + debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") + exe_name = "{0}/../sram_char.py ".format(OPENRAM_HOME) + else: + exe_name = "{0}{1}/../sram_char.py ".format(OPTS.coverage_exe, OPENRAM_HOME) + config_name = "{0}/tests/configs/config_mem_char_func.py".format(OPENRAM_HOME) + cmd = "{0} -n -o {1} -p {2} {3} {4} {5} 2>&1 > {6}/output.log".format(exe_name, + out_file, + out_path, + options, + config_name, + sp_file, + out_path) + debug.info(1, cmd) + os.system(cmd) + + # grep any errors from the output + output_log = open("{0}/{1}.log".format(out_path, out_file), "r") + output = output_log.read() + output_log.close() + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) + + # now clean up the directory + if not OPTS.keep_temp: + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + openram.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/30_openram_sram_func_test.py b/compiler/tests/30_openram_sram_func_test.py new file mode 100755 index 00000000..3a5d6283 --- /dev/null +++ b/compiler/tests/30_openram_sram_func_test.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import sys, os, re +import shutil +import getpass +import unittest +from testutils import * + +import openram +from openram import debug +from openram import OPTS + + +class sram_func_test(openram_test): + + def runTest(self): + global OPTS + out_file = "sram_2_16_1_{0}".format(OPTS.tech_name) + out_path = "{0}/testsram_{1}_{2}_{3}".format(OPTS.openram_temp, OPTS.tech_name, getpass.getuser(), os.getpid()) + OPTS.output_name = out_file + OPTS.output_path = out_path + + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + config_file = "{}/tests/configs/config_mem_char_func".format(OPENRAM_HOME) + + openram.init_openram(config_file, is_unit_test=False) + sp_file = "{0}/tests/sp_files/{1}.sp".format(OPENRAM_HOME, OPTS.output_name) + + debug.info(1, "Testing commandline functional simulator script sram_char.py with 2-bit, 16 word SRAM.") + + # make sure we start without the files existing + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + try: + os.makedirs(out_path, 0o0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(out_path, 0o0750) + + # specify the same verbosity for the system call + options = "" + for i in range(OPTS.verbose_level): + options += " -v" + + if OPTS.spice_name: + options += " -s {}".format(OPTS.spice_name) + + if OPTS.tech_name: + options += " -t {}".format(OPTS.tech_name) + + options += " -j 2" + + # Always perform code coverage + if OPTS.coverage == 0: + debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") + exe_name = "{0}/../sram_func.py ".format(OPENRAM_HOME) + else: + exe_name = "{0}{1}/../sram_func.py ".format(OPTS.coverage_exe, OPENRAM_HOME) + config_name = "{0}/tests/configs/config_mem_char_func.py".format(OPENRAM_HOME) + period_and_cycles = 10 + cmd = "{0} -n -o {1} -p {2} {3} {4} {5} {6} {6} 2>&1 > {7}/output.log".format(exe_name, + out_file, + out_path, + options, + config_name, + sp_file, + period_and_cycles, + out_path) + debug.info(1, cmd) + os.system(cmd) + + # grep any errors from the output + output_log = open("{0}/output.log".format(out_path), "r") + output = output_log.read() + output_log.close() + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) + + # now clean up the directory + if not OPTS.keep_temp: + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + openram.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = openram.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile index 5fd0929c..7f1bef57 100644 --- a/compiler/tests/Makefile +++ b/compiler/tests/Makefile @@ -180,6 +180,8 @@ BROKEN_STAMPS = \ sky130/30_openram_back_end_test.ok \ sky130/30_openram_front_end_library_test.ok \ sky130/30_openram_front_end_test.ok \ + sky130/30_openram_sram_char_test.ok \ + sky130/30_openram_sram_func_test.ok \ gettech = $(word 1,$(subst /, ,$*)) getfile = $(word 2,$(subst /, ,$*)) diff --git a/compiler/characterizer/bit_polarity.py b/compiler/tests/configs/config_mem_char_func.py similarity index 52% rename from compiler/characterizer/bit_polarity.py rename to compiler/tests/configs/config_mem_char_func.py index 5a6b6a06..8c0b09b9 100644 --- a/compiler/characterizer/bit_polarity.py +++ b/compiler/tests/configs/config_mem_char_func.py @@ -5,10 +5,17 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # +from openram import OPTS +word_size = 2 +num_words = 16 -from enum import Enum +tech_name = OPTS.tech_name +if tech_name == "sky130": + num_spare_cols = 1 + num_spare_rows = 1 -class bit_polarity(Enum): - NONINVERTING = 0 - INVERTING = 1 +output_name = "sram" +analytical_delay = False +nominal_corner_only = True +spice_name = "Xyce" diff --git a/compiler/tests/golden/sram_2_16_1_sky130.sp b/compiler/tests/golden/sram_2_16_1_sky130.sp new file mode 100644 index 00000000..d68dde96 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_sky130.sp @@ -0,0 +1,2400 @@ +************************************************** +* OpenRAM generated memory. +* Words: 16 +* Data bits: 2 +* Banks: 1 +* Column mux: 1:1 +* Trimmed: False +* LVS: False +************************************************** +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +* SPICE3 file created from sky130_fd_bd_sram__openram_dff.ext - technology: EFS8A + +.subckt sky130_fd_bd_sram__openram_dff D Q CLK VDD GND +X1000 a_511_725# a_n8_115# VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1001 a_353_115# CLK a_11_624# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1002 a_353_725# a_203_89# a_11_624# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1003 a_11_624# a_203_89# a_161_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1004 a_11_624# CLK a_161_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1005 GND Q a_703_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1006 VDD Q a_703_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1007 a_203_89# CLK GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1008 a_203_89# CLK VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1009 a_161_115# D GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1010 a_161_725# D VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1011 GND a_11_624# a_n8_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1012 a_703_115# a_203_89# ON GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1013 VDD a_11_624# a_n8_115# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1014 a_703_725# CLK ON VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1015 Q ON VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1016 Q ON GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1017 ON a_203_89# a_511_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1018 ON CLK a_511_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1019 GND a_n8_115# a_353_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1020 VDD a_n8_115# a_353_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1021 a_511_115# a_n8_115# GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +.ends + +.SUBCKT sram_2_16_1_sky130_row_addr_dff ++ din_0 din_1 din_2 din_3 din_4 dout_0 dout_1 dout_2 dout_3 dout_4 clk ++ vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* INPUT : din_3 +* INPUT : din_4 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* OUTPUT: dout_3 +* OUTPUT: dout_4 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 5 cols: 1 +Xdff_r0_c0 ++ din_0 dout_0 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r1_c0 ++ din_1 dout_1 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r2_c0 ++ din_2 dout_2 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r3_c0 ++ din_3 dout_3 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r4_c0 ++ din_4 dout_4 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +.ENDS sram_2_16_1_sky130_row_addr_dff + +.SUBCKT sram_2_16_1_sky130_data_dff ++ din_0 din_1 din_2 dout_0 dout_1 dout_2 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 1 cols: 3 +Xdff_r0_c0 ++ din_0 dout_0 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r0_c1 ++ din_1 dout_1 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r0_c2 ++ din_2 dout_2 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +.ENDS sram_2_16_1_sky130_data_dff + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u + +.SUBCKT sram_2_16_1_sky130_pinv_dec ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_dec +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_nand2_dec.ext - technology: EFS8A + + +* Top level circuit sky130_fd_bd_sram__openram_sp_nand2_dec +.subckt sky130_fd_bd_sram__openram_sp_nand2_dec A B Z VDD GND + +X1001 Z B VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1002 VDD A Z VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1000 Z A a_n722_276# GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1003 a_n722_276# B GND GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +.ends + + +.SUBCKT sram_2_16_1_sky130_and2_dec ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand2_dec +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_and2_dec + +.SUBCKT sram_2_16_1_sky130_hierarchical_predecode2x4 ++ in_0 in_1 out_0 out_1 out_2 out_3 vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* OUTPUT: out_0 +* OUTPUT: out_1 +* OUTPUT: out_2 +* OUTPUT: out_3 +* POWER : vdd +* GROUND: gnd +Xpre_inv_0 ++ in_0 inbar_0 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +Xpre_inv_1 ++ in_1 inbar_1 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +XXpre2x4_and_0 ++ inbar_0 inbar_1 out_0 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XXpre2x4_and_1 ++ in_0 inbar_1 out_1 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XXpre2x4_and_2 ++ inbar_0 in_1 out_2 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XXpre2x4_and_3 ++ in_0 in_1 out_3 vdd gnd ++ sram_2_16_1_sky130_and2_dec +.ENDS sram_2_16_1_sky130_hierarchical_predecode2x4 +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_nand3_dec.ext - technology: EFS8A + + +* Top level circuit sky130_fd_bd_sram__openram_sp_nand3_dec +.subckt sky130_fd_bd_sram__openram_sp_nand3_dec A B C Z VDD GND + +X1001 Z A a_n346_328# GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1002 a_n346_256# C GND GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1003 a_n346_328# B a_n346_256# GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1000 Z B VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1004 Z A VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1005 Z C VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +.ends + + +.SUBCKT sram_2_16_1_sky130_and3_dec ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand3_dec_nand ++ A B C zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand3_dec +Xpand3_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_and3_dec + +.SUBCKT sram_2_16_1_sky130_hierarchical_predecode3x8 ++ in_0 in_1 in_2 out_0 out_1 out_2 out_3 out_4 out_5 out_6 out_7 vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* INPUT : in_2 +* OUTPUT: out_0 +* OUTPUT: out_1 +* OUTPUT: out_2 +* OUTPUT: out_3 +* OUTPUT: out_4 +* OUTPUT: out_5 +* OUTPUT: out_6 +* OUTPUT: out_7 +* POWER : vdd +* GROUND: gnd +Xpre_inv_0 ++ in_0 inbar_0 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +Xpre_inv_1 ++ in_1 inbar_1 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +Xpre_inv_2 ++ in_2 inbar_2 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +XXpre3x8_and_0 ++ inbar_0 inbar_1 inbar_2 out_0 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_1 ++ in_0 inbar_1 inbar_2 out_1 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_2 ++ inbar_0 in_1 inbar_2 out_2 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_3 ++ in_0 in_1 inbar_2 out_3 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_4 ++ inbar_0 inbar_1 in_2 out_4 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_5 ++ in_0 inbar_1 in_2 out_5 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_6 ++ inbar_0 in_1 in_2 out_6 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_7 ++ in_0 in_1 in_2 out_7 vdd gnd ++ sram_2_16_1_sky130_and3_dec +.ENDS sram_2_16_1_sky130_hierarchical_predecode3x8 + +.SUBCKT sram_2_16_1_sky130_hierarchical_decoder ++ addr_0 addr_1 addr_2 addr_3 addr_4 decode_0 decode_1 decode_2 decode_3 ++ decode_4 decode_5 decode_6 decode_7 decode_8 decode_9 decode_10 ++ decode_11 decode_12 decode_13 decode_14 decode_15 decode_16 vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* INPUT : addr_4 +* OUTPUT: decode_0 +* OUTPUT: decode_1 +* OUTPUT: decode_2 +* OUTPUT: decode_3 +* OUTPUT: decode_4 +* OUTPUT: decode_5 +* OUTPUT: decode_6 +* OUTPUT: decode_7 +* OUTPUT: decode_8 +* OUTPUT: decode_9 +* OUTPUT: decode_10 +* OUTPUT: decode_11 +* OUTPUT: decode_12 +* OUTPUT: decode_13 +* OUTPUT: decode_14 +* OUTPUT: decode_15 +* OUTPUT: decode_16 +* POWER : vdd +* GROUND: gnd +Xpre_0 ++ addr_0 addr_1 out_0 out_1 out_2 out_3 vdd gnd ++ sram_2_16_1_sky130_hierarchical_predecode2x4 +Xpre3x8_0 ++ addr_2 addr_3 addr_4 out_4 out_5 out_6 out_7 out_8 out_9 out_10 out_11 ++ vdd gnd ++ sram_2_16_1_sky130_hierarchical_predecode3x8 +XDEC_AND_0 ++ out_0 out_4 decode_0 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_4 ++ out_0 out_5 decode_4 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_8 ++ out_0 out_6 decode_8 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_12 ++ out_0 out_7 decode_12 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_16 ++ out_0 out_8 decode_16 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_1 ++ out_1 out_4 decode_1 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_5 ++ out_1 out_5 decode_5 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_9 ++ out_1 out_6 decode_9 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_13 ++ out_1 out_7 decode_13 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_2 ++ out_2 out_4 decode_2 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_6 ++ out_2 out_5 decode_6 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_10 ++ out_2 out_6 decode_10 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_14 ++ out_2 out_7 decode_14 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_3 ++ out_3 out_4 decode_3 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_7 ++ out_3 out_5 decode_7 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_11 ++ out_3 out_6 decode_11 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_15 ++ out_3 out_7 decode_15 vdd gnd ++ sram_2_16_1_sky130_and2_dec +.ENDS sram_2_16_1_sky130_hierarchical_decoder + +.SUBCKT sram_2_16_1_sky130_and2_dec_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand2_dec +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_and2_dec_0 + +.SUBCKT sram_2_16_1_sky130_wordline_driver ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* cols: 3 +Xwld_nand ++ A B zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand2_dec +Xwl_driver ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_wordline_driver + +.SUBCKT sram_2_16_1_sky130_wordline_driver_array ++ in_0 in_1 in_2 in_3 in_4 in_5 in_6 in_7 in_8 in_9 in_10 in_11 in_12 ++ in_13 in_14 in_15 in_16 wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 wl_7 wl_8 ++ wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_16 en vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* INPUT : in_2 +* INPUT : in_3 +* INPUT : in_4 +* INPUT : in_5 +* INPUT : in_6 +* INPUT : in_7 +* INPUT : in_8 +* INPUT : in_9 +* INPUT : in_10 +* INPUT : in_11 +* INPUT : in_12 +* INPUT : in_13 +* INPUT : in_14 +* INPUT : in_15 +* INPUT : in_16 +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* OUTPUT: wl_16 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +Xwl_driver_and0 ++ in_0 en wl_0 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and1 ++ in_1 en wl_1 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and2 ++ in_2 en wl_2 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and3 ++ in_3 en wl_3 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and4 ++ in_4 en wl_4 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and5 ++ in_5 en wl_5 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and6 ++ in_6 en wl_6 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and7 ++ in_7 en wl_7 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and8 ++ in_8 en wl_8 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and9 ++ in_9 en wl_9 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and10 ++ in_10 en wl_10 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and11 ++ in_11 en wl_11 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and12 ++ in_12 en wl_12 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and13 ++ in_13 en wl_13 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and14 ++ in_14 en wl_14 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and15 ++ in_15 en wl_15 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and16 ++ in_16 en wl_16 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +.ENDS sram_2_16_1_sky130_wordline_driver_array + +.SUBCKT sram_2_16_1_sky130_port_address ++ addr_0 addr_1 addr_2 addr_3 addr_4 wl_en wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 ++ wl_6 wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_16 rbl_wl ++ vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* INPUT : addr_4 +* INPUT : wl_en +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* OUTPUT: wl_16 +* OUTPUT: rbl_wl +* POWER : vdd +* GROUND: gnd +Xrow_decoder ++ addr_0 addr_1 addr_2 addr_3 addr_4 dec_out_0 dec_out_1 dec_out_2 ++ dec_out_3 dec_out_4 dec_out_5 dec_out_6 dec_out_7 dec_out_8 dec_out_9 ++ dec_out_10 dec_out_11 dec_out_12 dec_out_13 dec_out_14 dec_out_15 ++ dec_out_16 vdd gnd ++ sram_2_16_1_sky130_hierarchical_decoder +Xwordline_driver ++ dec_out_0 dec_out_1 dec_out_2 dec_out_3 dec_out_4 dec_out_5 dec_out_6 ++ dec_out_7 dec_out_8 dec_out_9 dec_out_10 dec_out_11 dec_out_12 ++ dec_out_13 dec_out_14 dec_out_15 dec_out_16 wl_0 wl_1 wl_2 wl_3 wl_4 ++ wl_5 wl_6 wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_16 ++ wl_en vdd gnd ++ sram_2_16_1_sky130_wordline_driver_array +Xrbl_driver ++ wl_en vdd rbl_wl vdd gnd ++ sram_2_16_1_sky130_and2_dec_0 +.ENDS sram_2_16_1_sky130_port_address +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colenda_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colenda_cent VPWR VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colenda.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colenda bl vdd gnd br gate vpb vnb +*X0 br gate br vnb sky130_fd_pr__special_nfet_pass w=0.065u l=0.17u +.ends + +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colenda_p_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colenda_p_cent VGND VPB VNB +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_col_cap_array_0 ++ fake_bl_0 fake_br_0 fake_bl_1 fake_br_1 fake_bl_2 fake_br_2 vdd gnd ++ gate +* OUTPUT: fake_bl_0 +* OUTPUT: fake_br_0 +* OUTPUT: fake_bl_1 +* OUTPUT: fake_br_1 +* OUTPUT: fake_bl_2 +* OUTPUT: fake_br_2 +* POWER : vdd +* GROUND: gnd +* BIAS : gate +Xrca_bottom_0 ++ fake_bl_0 vdd gnd fake_br_0 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +Xrca_bottom_1 ++ vdd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda_cent +Xrca_bottom_2 ++ fake_bl_1 vdd gnd fake_br_1 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +Xrca_bottom_3 ++ gnd vdd vnb ++ sky130_fd_bd_sram__sram_sp_colenda_p_cent +Xrca_bottom_4 ++ fake_bl_2 vdd gnd fake_br_2 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +.ENDS sram_2_16_1_sky130_sky130_col_cap_array_0 +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrap_p.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrap_p VGND +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_cell_opt1a_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_cell_opt1a_replica BL BR VGND VPWR VPB VNB WL +X0 VPWR WL BR VNB sky130_fd_pr__special_nfet_pass ad=4.375e+10p pd=920000u as=1.68e+10p ps=520000u w=140000u l=150000u +X1 Q VPWR VGND VNB sky130_fd_pr__special_nfet_latch ad=1.56e+11p pd=2.38e+06u as=8.08e+10p ps=1.28e+06u w=210000u l=150000u +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass ad=1.68e+10p pd=520000u as=4.25e+10p ps=920000u w=140000u l=150000u +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass ad=3.5e+10p pd=780000u as=0p ps=0u w=70000u l=95000u +*X4 VPWR WL VPWR VPB sky130_fd_pr__special_pfet_pass ad=9.72e+10p pd=1.86e+06u as=0p ps=0u w=70000u l=95000u +X5 VPWR Q VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X6 Q VPWR VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X7 VGND Q VPWR VNB sky130_fd_pr__special_nfet_latch ad=0p pd=0u as=0p ps=0u w=210000u l=150000u +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colend_p_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colend_p_cent VGND VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrapa_p.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrapa_p VGND +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_cell_opt1_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_cell_opt1_replica BL BR VGND VPWR VPB VNB WL +X0 VPWR WL BR VNB sky130_fd_pr__special_nfet_pass ad=4.375e+10p pd=920000u as=1.68e+10p ps=520000u w=140000u l=150000u +X1 Q VPWR VGND VNB sky130_fd_pr__special_nfet_latch ad=1.56e+11p pd=2.38e+06u as=8.08e+10p ps=1.28e+06u w=210000u l=150000u +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass ad=1.68e+10p pd=520000u as=4.25e+10p ps=920000u w=140000u l=150000u +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass ad=3.5e+10p pd=780000u as=0p ps=0u w=70000u l=95000u +*X4 VPWR WL VPWR VPB sky130_fd_pr__special_pfet_pass ad=9.72e+10p pd=1.86e+06u as=0p ps=0u w=70000u l=95000u +X5 VPWR Q VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X6 Q VPWR VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X7 VGND Q VPWR VNB sky130_fd_pr__special_nfet_latch ad=0p pd=0u as=0p ps=0u w=210000u l=150000u +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colend.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colend bl vdd gnd br gate vpb vnb +*X0 br gate br vnb sky130_fd_pr__special_nfet_pass w=0.07u l=0.21u +.ends + + +.SUBCKT sram_2_16_1_sky130_sky130_replica_column ++ bl_0_0 br_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 ++ wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 wl_0_17 ++ wl_0_18 vdd gnd top_gate bot_gate +* OUTPUT: bl_0_0 +* OUTPUT: br_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* INPUT : wl_0_17 +* INPUT : wl_0_18 +* POWER : vdd +* GROUND: gnd +* INPUT : top_gate +* INPUT : bot_gate +Xrbc_0 ++ bl_0_0 vdd gnd br_0_0 top_gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +Xrbc_0_cap ++ gnd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend_p_cent +Xrbc_1 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_1_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_2 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_2_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_3 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_3_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_4 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_4_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_5 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_5_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_6 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_6_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_7 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_7_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_8 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_8_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_9 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_9_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_10 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_10_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_11 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_11_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_12 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_12_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_13 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_13_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_14 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_14_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_15 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_15_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_16 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_16_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_17 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_17 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_17_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_18 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_18 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_18_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_19 ++ bl_0_0 vdd gnd br_0_0 bot_gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +Xrbc_19_cap ++ gnd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda_p_cent +.ENDS sram_2_16_1_sky130_sky130_replica_column +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colend_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colend_cent VPWR VPB VNB +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_col_cap_array ++ fake_bl_0 fake_br_0 fake_bl_1 fake_br_1 fake_bl_2 fake_br_2 vdd gnd ++ gate +* OUTPUT: fake_bl_0 +* OUTPUT: fake_br_0 +* OUTPUT: fake_bl_1 +* OUTPUT: fake_br_1 +* OUTPUT: fake_bl_2 +* OUTPUT: fake_br_2 +* POWER : vdd +* GROUND: gnd +* BIAS : gate +Xrca_top_0 ++ fake_bl_0 vdd gnd fake_br_0 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +Xrca_top_1 ++ vdd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend_cent +Xrca_top_2 ++ fake_bl_1 vdd gnd fake_br_1 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +Xrca_top_3 ++ gnd vdd vnb ++ sky130_fd_bd_sram__sram_sp_colend_p_cent +Xrca_top_4 ++ fake_bl_2 vdd gnd fake_br_2 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +.ENDS sram_2_16_1_sky130_sky130_col_cap_array +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrapa.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrapa VPWR +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy BL BR VGND VPWR VPB VNB WL +X0 ll WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +X1 ul Q_bar_float VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +X2 BL WL ul VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +X5 VPWR Q_float lr VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X6 ur Q_bar_float VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X7 VGND Q_float ll VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_dummy_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xrow_0_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy +Xrow_0_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_0_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy +Xrow_0_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_0_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy +.ENDS sram_2_16_1_sky130_sky130_dummy_array +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cornera.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cornera VNB VPWR VPB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cornerb.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cornerb VPWR VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_rowenda.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_rowenda VPWR WL +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_rowend.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_rowend VPWR WL +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_row_cap_array_0 ++ wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 ++ wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 wl_0_17 ++ wl_0_18 wl_0_19 vdd gnd +* OUTPUT: wl_0_0 +* OUTPUT: wl_0_1 +* OUTPUT: wl_0_2 +* OUTPUT: wl_0_3 +* OUTPUT: wl_0_4 +* OUTPUT: wl_0_5 +* OUTPUT: wl_0_6 +* OUTPUT: wl_0_7 +* OUTPUT: wl_0_8 +* OUTPUT: wl_0_9 +* OUTPUT: wl_0_10 +* OUTPUT: wl_0_11 +* OUTPUT: wl_0_12 +* OUTPUT: wl_0_13 +* OUTPUT: wl_0_14 +* OUTPUT: wl_0_15 +* OUTPUT: wl_0_16 +* OUTPUT: wl_0_17 +* OUTPUT: wl_0_18 +* OUTPUT: wl_0_19 +* POWER : vdd +* GROUND: gnd +Xrca_0 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_cornera +Xrca_1 ++ wl_0_0 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_2 ++ wl_0_1 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_3 ++ wl_0_2 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_4 ++ wl_0_3 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_5 ++ wl_0_4 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_6 ++ wl_0_5 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_7 ++ wl_0_6 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_8 ++ wl_0_7 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_9 ++ wl_0_8 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_10 ++ wl_0_9 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_11 ++ wl_0_10 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_12 ++ wl_0_11 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_13 ++ wl_0_12 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_14 ++ wl_0_13 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_15 ++ wl_0_14 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_16 ++ wl_0_15 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_17 ++ wl_0_16 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_18 ++ wl_0_17 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_19 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_cornerb +.ENDS sram_2_16_1_sky130_sky130_row_cap_array_0 +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cell_opt1a.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cell_opt1a BL BR VGND VPWR VPB VNB WL +X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cell_opt1.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cell_opt1 BL BR VGND VPWR VPB VNB WL +X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrap.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrap VPWR +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_bitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 wl_0_1 wl_0_2 wl_0_3 ++ wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 ++ wl_0_13 wl_0_14 wl_0_15 wl_0_16 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +Xrow_0_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_0_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_0_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_0_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_0_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_1_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_1_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_1_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_1_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_1_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_2_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_2_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_2_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_2_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_2_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_3_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_3_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_3_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_3_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_3_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_4_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_4_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_4_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_4_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_4_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_5_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_5_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_5_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_5_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_5_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_6_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_6_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_6_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_6_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_6_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_7_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_7_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_7_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_7_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_7_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_8_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_8_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_8_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_8_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_8_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_9_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_9_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_9_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_9_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_9_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_10_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_10_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_10_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_10_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_10_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_11_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_11_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_11_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_11_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_11_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_12_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_12_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_12_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_12_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_12_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_13_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_13_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_13_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_13_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_13_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_14_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_14_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_14_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_14_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_14_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_15_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_15_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_15_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_15_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_15_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_16_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_16_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_16_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_16_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_16_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +.ENDS sram_2_16_1_sky130_sky130_bitcell_array +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_corner.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_corner VPWR VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_rowenda_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_rowenda_replica VPWR WL +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_rowend_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_rowend_replica VPWR WL +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_row_cap_array ++ wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 ++ wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 wl_0_17 ++ wl_0_18 wl_0_19 vdd gnd +* OUTPUT: wl_0_0 +* OUTPUT: wl_0_1 +* OUTPUT: wl_0_2 +* OUTPUT: wl_0_3 +* OUTPUT: wl_0_4 +* OUTPUT: wl_0_5 +* OUTPUT: wl_0_6 +* OUTPUT: wl_0_7 +* OUTPUT: wl_0_8 +* OUTPUT: wl_0_9 +* OUTPUT: wl_0_10 +* OUTPUT: wl_0_11 +* OUTPUT: wl_0_12 +* OUTPUT: wl_0_13 +* OUTPUT: wl_0_14 +* OUTPUT: wl_0_15 +* OUTPUT: wl_0_16 +* OUTPUT: wl_0_17 +* OUTPUT: wl_0_18 +* OUTPUT: wl_0_19 +* POWER : vdd +* GROUND: gnd +Xrca_0 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_cornera +Xrca_1 ++ wl_0_0 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_2 ++ wl_0_1 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_3 ++ wl_0_2 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_4 ++ wl_0_3 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_5 ++ wl_0_4 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_6 ++ wl_0_5 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_7 ++ wl_0_6 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_8 ++ wl_0_7 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_9 ++ wl_0_8 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_10 ++ wl_0_9 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_11 ++ wl_0_10 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_12 ++ wl_0_11 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_13 ++ wl_0_12 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_14 ++ wl_0_13 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_15 ++ wl_0_14 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_16 ++ wl_0_15 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_17 ++ wl_0_16 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_18 ++ wl_0_17 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_19 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_corner +.ENDS sram_2_16_1_sky130_sky130_row_cap_array + +.SUBCKT sram_2_16_1_sky130_sky130_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xbitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 wl_0_1 wl_0_2 wl_0_3 ++ wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 ++ wl_0_13 wl_0_14 wl_0_15 wl_0_16 vdd gnd ++ sram_2_16_1_sky130_sky130_bitcell_array +Xreplica_col_0 ++ rbl_bl_0_0 rbl_br_0_0 rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 ++ wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 ++ wl_0_14 wl_0_15 wl_0_16 vdd gnd gnd gnd ++ sram_2_16_1_sky130_sky130_replica_column +Xdummy_row_0 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 rbl_wl_0_0 vdd gnd ++ sram_2_16_1_sky130_sky130_dummy_array +Xdummy_row_bot ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 vdd gnd gnd ++ sram_2_16_1_sky130_sky130_col_cap_array_0 +Xdummy_row_top ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 vdd gnd gnd ++ sram_2_16_1_sky130_sky130_col_cap_array +Xdummy_col_left ++ gnd rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ gnd vdd gnd ++ sram_2_16_1_sky130_sky130_row_cap_array +Xdummy_col_right ++ gnd rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ gnd vdd gnd ++ sram_2_16_1_sky130_sky130_row_cap_array_0 +.ENDS sram_2_16_1_sky130_sky130_replica_bitcell_array + +.SUBCKT sram_2_16_1_sky130_sky130_capped_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xreplica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd ++ sram_2_16_1_sky130_sky130_replica_bitcell_array +.ENDS sram_2_16_1_sky130_sky130_capped_replica_bitcell_array +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +*********************** "sky130_fd_bd_sram__openram_write_driver" ****************************** + +.SUBCKT sky130_fd_bd_sram__openram_write_driver DIN BL BR EN VDD GND + +**** Inverter to conver Data_in to data_in_bar ****** +* din_bar = inv(DIN) +X_1 din_bar DIN GND GND sky130_fd_pr__nfet_01v8 W=0.36 L=0.15 +X_2 din_bar DIN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 + +**** 2input nand gate follwed by inverter to drive BL ****** +* din_bar_gated = nand(EN, DIN) +X_3 din_bar_gated EN net_7 GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_4 net_7 DIN GND GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_5 din_bar_gated EN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_6 din_bar_gated DIN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +* din_bar_gated_bar = inv(din_bar_gated) +X_7 din_bar_gated_bar din_bar_gated VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_8 din_bar_gated_bar din_bar_gated GND GND sky130_fd_pr__nfet_01v8 W=0.36 L=0.15 + +**** 2input nand gate follwed by inverter to drive BR****** +* din_gated = nand(EN, din_bar) +X_9 din_gated EN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_10 din_gated EN net_8 GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_11 net_8 din_bar GND GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_12 din_gated din_bar VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +* din_gated_bar = inv(din_gated) +X_13 din_gated_bar din_gated VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_14 din_gated_bar din_gated GND GND sky130_fd_pr__nfet_01v8 W=0.36 L=0.15 + +************************************************ +* pull down with EN enable +X_15 BL din_gated_bar GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X_16 BR din_bar_gated_bar GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 + +.ENDS sky130_fd_bd_sram__openram_write_driver + +.SUBCKT sram_2_16_1_sky130_write_driver_array ++ data_0 data_1 data_2 bl_0 br_0 bl_1 br_1 bl_2 br_2 en_0 en_1 vdd gnd +* INPUT : data_0 +* INPUT : data_1 +* INPUT : data_2 +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* OUTPUT: bl_2 +* OUTPUT: br_2 +* INPUT : en_0 +* INPUT : en_1 +* POWER : vdd +* GROUND: gnd +* columns: 2 +* word_size 2 +Xwrite_driver0 ++ data_0 bl_0 br_0 en_0 vdd gnd ++ sky130_fd_bd_sram__openram_write_driver +Xwrite_driver1 ++ data_1 bl_1 br_1 en_0 vdd gnd ++ sky130_fd_bd_sram__openram_write_driver +Xwrite_driver2 ++ data_2 bl_2 br_2 en_1 vdd gnd ++ sky130_fd_bd_sram__openram_write_driver +.ENDS sram_2_16_1_sky130_write_driver_array + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u + +.SUBCKT sram_2_16_1_sky130_precharge_0 ++ bl br en_bar vdd +* OUTPUT: bl +* OUTPUT: br +* INPUT : en_bar +* POWER : vdd +Xlower_pmos bl en_bar br vdd sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u +Xupper_pmos1 bl en_bar vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u +Xupper_pmos2 br en_bar vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u +.ENDS sram_2_16_1_sky130_precharge_0 + +.SUBCKT sram_2_16_1_sky130_precharge_array ++ bl_0 br_0 bl_1 br_1 bl_2 br_2 bl_3 br_3 en_bar vdd +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* OUTPUT: bl_2 +* OUTPUT: br_2 +* OUTPUT: bl_3 +* OUTPUT: br_3 +* INPUT : en_bar +* POWER : vdd +* cols: 4 size: 1 bl: bl br: br +Xpre_column_0 ++ bl_0 br_0 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +Xpre_column_1 ++ bl_1 br_1 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +Xpre_column_2 ++ bl_2 br_2 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +Xpre_column_3 ++ bl_3 br_3 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +.ENDS sram_2_16_1_sky130_precharge_array +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +*********************** "sky130_fd_bd_sram__openram_sense_amp" ****************************** + +.SUBCKT sky130_fd_bd_sram__openram_sense_amp BL BR DOUT EN VDD GND +X1000 GND EN a_56_432# GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 +X1001 a_56_432# dint_bar dint GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 +X1002 dint_bar dint a_56_432# GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 + +X1003 VDD dint_bar dint VDD sky130_fd_pr__pfet_01v8 W=1.26 L=0.15 +X1004 dint_bar dint VDD VDD sky130_fd_pr__pfet_01v8 W=1.26 L=0.15 + +X1005 BL EN dint VDD sky130_fd_pr__pfet_01v8 W=2 L=0.15 +X1006 dint_bar EN BR VDD sky130_fd_pr__pfet_01v8 W=2 L=0.15 + +X1007 VDD dint_bar DOUT VDD sky130_fd_pr__pfet_01v8 W=1.26 L=0.15 +X1008 DOUT dint_bar GND GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 + +.ENDS sky130_fd_bd_sram__openram_sense_amp + +.SUBCKT sram_2_16_1_sky130_sense_amp_array ++ data_0 bl_0 br_0 data_1 bl_1 br_1 data_2 bl_2 br_2 en vdd gnd +* OUTPUT: data_0 +* INPUT : bl_0 +* INPUT : br_0 +* OUTPUT: data_1 +* INPUT : bl_1 +* INPUT : br_1 +* OUTPUT: data_2 +* INPUT : bl_2 +* INPUT : br_2 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* word_size 2 +* words_per_row: 1 +Xsa_d0 ++ bl_0 br_0 data_0 en vdd gnd ++ sky130_fd_bd_sram__openram_sense_amp +Xsa_d1 ++ bl_1 br_1 data_1 en vdd gnd ++ sky130_fd_bd_sram__openram_sense_amp +Xsa_d2 ++ bl_2 br_2 data_2 en vdd gnd ++ sky130_fd_bd_sram__openram_sense_amp +.ENDS sram_2_16_1_sky130_sense_amp_array + +.SUBCKT sram_2_16_1_sky130_port_data ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 sparebl_0 sparebr_0 dout_0 dout_1 ++ dout_2 din_0 din_1 din_2 s_en p_en_bar w_en bank_spare_wen0 vdd gnd +* INOUT : rbl_bl +* INOUT : rbl_br +* INOUT : bl_0 +* INOUT : br_0 +* INOUT : bl_1 +* INOUT : br_1 +* INOUT : sparebl_0 +* INOUT : sparebr_0 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* INPUT : s_en +* INPUT : p_en_bar +* INPUT : w_en +* INPUT : bank_spare_wen0 +* POWER : vdd +* GROUND: gnd +Xprecharge_array0 ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 sparebl_0 sparebr_0 p_en_bar vdd ++ sram_2_16_1_sky130_precharge_array +Xsense_amp_array0 ++ dout_0 bl_0 br_0 dout_1 bl_1 br_1 dout_2 sparebl_0 sparebr_0 s_en vdd ++ gnd ++ sram_2_16_1_sky130_sense_amp_array +Xwrite_driver_array0 ++ din_0 din_1 din_2 bl_0 br_0 bl_1 br_1 sparebl_0 sparebr_0 w_en ++ bank_spare_wen0 vdd gnd ++ sram_2_16_1_sky130_write_driver_array +.ENDS sram_2_16_1_sky130_port_data + +.SUBCKT sram_2_16_1_sky130_bank ++ dout0_0 dout0_1 dout0_2 rbl_bl_0_0 din0_0 din0_1 din0_2 addr0_0 ++ addr0_1 addr0_2 addr0_3 addr0_4 s_en0 p_en_bar0 w_en0 ++ bank_spare_wen0_0 wl_en0 vdd gnd +* OUTPUT: dout0_0 +* OUTPUT: dout0_1 +* OUTPUT: dout0_2 +* OUTPUT: rbl_bl_0_0 +* INPUT : din0_0 +* INPUT : din0_1 +* INPUT : din0_2 +* INPUT : addr0_0 +* INPUT : addr0_1 +* INPUT : addr0_2 +* INPUT : addr0_3 +* INPUT : addr0_4 +* INPUT : s_en0 +* INPUT : p_en_bar0 +* INPUT : w_en0 +* INPUT : bank_spare_wen0_0 +* INPUT : wl_en0 +* POWER : vdd +* GROUND: gnd +Xbitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 ++ wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 vdd gnd ++ sram_2_16_1_sky130_sky130_capped_replica_bitcell_array +Xport_data0 ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ dout0_0 dout0_1 dout0_2 din0_0 din0_1 din0_2 s_en0 p_en_bar0 w_en0 ++ bank_spare_wen0_0 vdd gnd ++ sram_2_16_1_sky130_port_data +Xport_address0 ++ addr0_0 addr0_1 addr0_2 addr0_3 addr0_4 wl_en0 wl_0_0 wl_0_1 wl_0_2 ++ wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 ++ wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 rbl_wl0 vdd gnd ++ sram_2_16_1_sky130_port_address +.ENDS sram_2_16_1_sky130_bank + +.SUBCKT sram_2_16_1_sky130_pinv_4 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_4 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +.SUBCKT sram_2_16_1_sky130_pinv_8 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 5 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +.ENDS sram_2_16_1_sky130_pinv_8 + +.SUBCKT sram_2_16_1_sky130_pdriver_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 5] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_sky130_pinv_4 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_8 +.ENDS sram_2_16_1_sky130_pdriver_1 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +.SUBCKT sram_2_16_1_sky130_pinv_9 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 10 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +.ENDS sram_2_16_1_sky130_pinv_9 + +.SUBCKT sram_2_16_1_sky130_pdriver_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [10] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_sky130_pinv_9 +.ENDS sram_2_16_1_sky130_pdriver_2 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +.SUBCKT sram_2_16_1_sky130_pnand3 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpnand3_pmos1 vdd A Z vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand3_pmos2 Z B vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand3_pmos3 Z C vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand3_nmos1 Z C net1 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand3_nmos2 net1 B net2 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand3_nmos3 net2 A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pnand3 + +.SUBCKT sram_2_16_1_sky130_pand3 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 10 +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_sky130_pnand3 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pdriver_2 +.ENDS sram_2_16_1_sky130_pand3 + +.SUBCKT sram_2_16_1_sky130_pdriver_4 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 1] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_sky130_pinv_4 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_4 +.ENDS sram_2_16_1_sky130_pdriver_4 + +.SUBCKT sram_2_16_1_sky130_pnand2_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpnand2_pmos1 vdd A Z vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_pmos2 Z B vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_nmos1 Z B net1 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand2_nmos2 net1 A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pnand2_0 + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=7 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=7 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +.SUBCKT sram_2_16_1_sky130_pinv_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 12 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=7 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=7 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +.ENDS sram_2_16_1_sky130_pinv_1 + +.SUBCKT sram_2_16_1_sky130_pdriver ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [12] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_sky130_pinv_1 +.ENDS sram_2_16_1_sky130_pdriver + +.SUBCKT sram_2_16_1_sky130_pnand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpnand2_pmos1 vdd A Z vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_pmos2 Z B vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_nmos1 Z B net1 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand2_nmos2 net1 A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pnand2 + +.SUBCKT sram_2_16_1_sky130_pand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 12 +Xpand2_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_sky130_pnand2 +Xpand2_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pdriver +.ENDS sram_2_16_1_sky130_pand2 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=10 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=10 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +.SUBCKT sram_2_16_1_sky130_pinv_7 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 17 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=10 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=10 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +.ENDS sram_2_16_1_sky130_pinv_7 + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=4 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=4 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u + +.SUBCKT sram_2_16_1_sky130_pinv_6 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 6 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=4 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=4 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +.ENDS sram_2_16_1_sky130_pinv_6 + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=2 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +.SUBCKT sram_2_16_1_sky130_pinv_5 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 2 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=2 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pinv_5 + +.SUBCKT sram_2_16_1_sky130_pdriver_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 2, 6, 17] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_sky130_pinv_4 +Xbuf_inv2 ++ Zb1_int Zb2_int vdd gnd ++ sram_2_16_1_sky130_pinv_5 +Xbuf_inv3 ++ Zb2_int Zb3_int vdd gnd ++ sram_2_16_1_sky130_pinv_6 +Xbuf_inv4 ++ Zb3_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_7 +.ENDS sram_2_16_1_sky130_pdriver_0 + +.SUBCKT sram_2_16_1_sky130_pinv_11 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_11 + +.SUBCKT sram_2_16_1_sky130_delay_chain ++ in out vdd gnd +* INPUT : in +* OUTPUT: out +* POWER : vdd +* GROUND: gnd +* fanouts: [4, 4, 4, 4, 4, 4, 4, 4, 4] +Xdinv0 ++ in dout_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_0 ++ dout_1 n_0_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_1 ++ dout_1 n_0_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_2 ++ dout_1 n_0_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_3 ++ dout_1 n_0_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv1 ++ dout_1 dout_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_0 ++ dout_2 n_1_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_1 ++ dout_2 n_1_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_2 ++ dout_2 n_1_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_3 ++ dout_2 n_1_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv2 ++ dout_2 dout_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_0 ++ dout_3 n_2_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_1 ++ dout_3 n_2_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_2 ++ dout_3 n_2_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_3 ++ dout_3 n_2_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv3 ++ dout_3 dout_4 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_0 ++ dout_4 n_3_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_1 ++ dout_4 n_3_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_2 ++ dout_4 n_3_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_3 ++ dout_4 n_3_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv4 ++ dout_4 dout_5 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_0 ++ dout_5 n_4_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_1 ++ dout_5 n_4_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_2 ++ dout_5 n_4_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_3 ++ dout_5 n_4_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv5 ++ dout_5 dout_6 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_0 ++ dout_6 n_5_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_1 ++ dout_6 n_5_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_2 ++ dout_6 n_5_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_3 ++ dout_6 n_5_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv6 ++ dout_6 dout_7 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_0 ++ dout_7 n_6_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_1 ++ dout_7 n_6_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_2 ++ dout_7 n_6_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_3 ++ dout_7 n_6_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv7 ++ dout_7 dout_8 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_0 ++ dout_8 n_7_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_1 ++ dout_8 n_7_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_2 ++ dout_8 n_7_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_3 ++ dout_8 n_7_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv8 ++ dout_8 out vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_0 ++ out n_8_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_1 ++ out n_8_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_2 ++ out n_8_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_3 ++ out n_8_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +.ENDS sram_2_16_1_sky130_delay_chain + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=2 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u + +.SUBCKT sram_2_16_1_sky130_pinv_10 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 3 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=2 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +.ENDS sram_2_16_1_sky130_pinv_10 + +.SUBCKT sram_2_16_1_sky130_pdriver_3 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [3] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_sky130_pinv_10 +.ENDS sram_2_16_1_sky130_pdriver_3 + +.SUBCKT sram_2_16_1_sky130_pand3_0 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 3 +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_sky130_pnand3 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pdriver_3 +.ENDS sram_2_16_1_sky130_pand3_0 + +.SUBCKT sram_2_16_1_sky130_pinv ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 2 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=2 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pinv + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +.SUBCKT sram_2_16_1_sky130_pinv_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 4 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +.ENDS sram_2_16_1_sky130_pinv_0 + +.SUBCKT sram_2_16_1_sky130_dff_buf_0 ++ D Q Qb clk vdd gnd +* INPUT : D +* OUTPUT: Q +* OUTPUT: Qb +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* inv1: 2 inv2: 4 +Xdff_buf_dff ++ D qint clk vdd gnd ++ sky130_fd_bd_sram__openram_dff +Xdff_buf_inv1 ++ qint Qb vdd gnd ++ sram_2_16_1_sky130_pinv +Xdff_buf_inv2 ++ Qb Q vdd gnd ++ sram_2_16_1_sky130_pinv_0 +.ENDS sram_2_16_1_sky130_dff_buf_0 + +.SUBCKT sram_2_16_1_sky130_dff_buf_array ++ din_0 din_1 dout_0 dout_bar_0 dout_1 dout_bar_1 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* OUTPUT: dout_0 +* OUTPUT: dout_bar_0 +* OUTPUT: dout_1 +* OUTPUT: dout_bar_1 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 2 cols: 1 +* inv1: 2 inv2: 4 +Xdff_r0_c0 ++ din_0 dout_0 dout_bar_0 clk vdd gnd ++ sram_2_16_1_sky130_dff_buf_0 +Xdff_r1_c0 ++ din_1 dout_1 dout_bar_1 clk vdd gnd ++ sram_2_16_1_sky130_dff_buf_0 +.ENDS sram_2_16_1_sky130_dff_buf_array + +.SUBCKT sram_2_16_1_sky130_pinv_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_2 + +.SUBCKT sram_2_16_1_sky130_control_logic_rw ++ csb web clk rbl_bl s_en w_en p_en_bar wl_en clk_buf vdd gnd +* INPUT : csb +* INPUT : web +* INPUT : clk +* INPUT : rbl_bl +* OUTPUT: s_en +* OUTPUT: w_en +* OUTPUT: p_en_bar +* OUTPUT: wl_en +* OUTPUT: clk_buf +* POWER : vdd +* GROUND: gnd +* num_rows: 17 +* words_per_row: 1 +* word_size 2 +Xctrl_dffs ++ csb web cs_bar cs we_bar we clk_buf vdd gnd ++ sram_2_16_1_sky130_dff_buf_array +Xclkbuf ++ clk clk_buf vdd gnd ++ sram_2_16_1_sky130_pdriver_0 +Xinv_clk_bar ++ clk_buf clk_bar vdd gnd ++ sram_2_16_1_sky130_pinv_2 +Xand2_gated_clk_bar ++ clk_bar cs gated_clk_bar vdd gnd ++ sram_2_16_1_sky130_pand2 +Xand2_gated_clk_buf ++ clk_buf cs gated_clk_buf vdd gnd ++ sram_2_16_1_sky130_pand2 +Xbuf_wl_en ++ gated_clk_bar wl_en vdd gnd ++ sram_2_16_1_sky130_pdriver_1 +Xrbl_bl_delay_inv ++ rbl_bl_delay rbl_bl_delay_bar vdd gnd ++ sram_2_16_1_sky130_pinv_2 +Xw_en_and ++ we rbl_bl_delay_bar gated_clk_bar w_en vdd gnd ++ sram_2_16_1_sky130_pand3 +Xbuf_s_en_and ++ rbl_bl_delay gated_clk_bar we_bar s_en vdd gnd ++ sram_2_16_1_sky130_pand3_0 +Xdelay_chain ++ rbl_bl rbl_bl_delay vdd gnd ++ sram_2_16_1_sky130_delay_chain +Xnand_p_en_bar ++ gated_clk_buf rbl_bl_delay p_en_bar_unbuf vdd gnd ++ sram_2_16_1_sky130_pnand2_0 +Xbuf_p_en_bar ++ p_en_bar_unbuf p_en_bar vdd gnd ++ sram_2_16_1_sky130_pdriver_4 +.ENDS sram_2_16_1_sky130_control_logic_rw + +.SUBCKT sram_2_16_1_sky130_spare_wen_dff ++ din_0 dout_0 clk vdd gnd +* INPUT : din_0 +* OUTPUT: dout_0 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 1 cols: 1 +Xdff_r0_c0 ++ din_0 dout_0 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +.ENDS sram_2_16_1_sky130_spare_wen_dff + +.SUBCKT sram_2_16_1_sky130 ++ din0[0] din0[1] din0[2] addr0[0] addr0[1] addr0[2] addr0[3] addr0[4] ++ csb0 web0 clk0 spare_wen0 dout0[0] dout0[1] dout0[2] vccd1 vssd1 +* INPUT : din0[0] +* INPUT : din0[1] +* INPUT : din0[2] +* INPUT : addr0[0] +* INPUT : addr0[1] +* INPUT : addr0[2] +* INPUT : addr0[3] +* INPUT : addr0[4] +* INPUT : csb0 +* INPUT : web0 +* INPUT : clk0 +* INPUT : spare_wen0 +* OUTPUT: dout0[0] +* OUTPUT: dout0[1] +* OUTPUT: dout0[2] +* POWER : vccd1 +* GROUND: vssd1 +Xbank0 ++ dout0[0] dout0[1] dout0[2] rbl_bl0 bank_din0_0 bank_din0_1 bank_din0_2 ++ a0_0 a0_1 a0_2 a0_3 a0_4 s_en0 p_en_bar0 w_en0 bank_spare_wen0_0 ++ wl_en0 vccd1 vssd1 ++ sram_2_16_1_sky130_bank +Xcontrol0 ++ csb0 web0 clk0 rbl_bl0 s_en0 w_en0 p_en_bar0 wl_en0 clk_buf0 vccd1 ++ vssd1 ++ sram_2_16_1_sky130_control_logic_rw +Xrow_address0 ++ addr0[0] addr0[1] addr0[2] addr0[3] addr0[4] a0_0 a0_1 a0_2 a0_3 a0_4 ++ clk_buf0 vccd1 vssd1 ++ sram_2_16_1_sky130_row_addr_dff +Xdata_dff0 ++ din0[0] din0[1] din0[2] bank_din0_0 bank_din0_1 bank_din0_2 clk_buf0 ++ vccd1 vssd1 ++ sram_2_16_1_sky130_data_dff +Xspare_wen_dff0 ++ spare_wen0[0] bank_spare_wen0_0 clk_buf0 vccd1 vssd1 ++ sram_2_16_1_sky130_spare_wen_dff +.ENDS sram_2_16_1_sky130 diff --git a/compiler/tests/sp_files/sram_2_16_1_freepdk45.sp b/compiler/tests/sp_files/sram_2_16_1_freepdk45.sp new file mode 100644 index 00000000..c638e514 --- /dev/null +++ b/compiler/tests/sp_files/sram_2_16_1_freepdk45.sp @@ -0,0 +1,1748 @@ +************************************************** +* OpenRAM generated memory. +* Words: 16 +* Data bits: 2 +* Banks: 1 +* Column mux: 1:1 +* Trimmed: False +* LVS: False +************************************************** +* File: DFFPOSX1.pex.netlist +* Created: Wed Jan 2 18:36:24 2008 +* Program "Calibre xRC" +* Version "v2007.2_34.24" +* +.subckt dff D Q clk vdd gnd +* +MM21 Q a_66_6# gnd gnd NMOS_VTG L=5e-08 W=5e-07 +MM19 a_76_6# a_2_6# a_66_6# gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM20 gnd Q a_76_6# gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM18 a_66_6# clk a_61_6# gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM17 a_61_6# a_34_4# gnd gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM10 gnd clk a_2_6# gnd NMOS_VTG L=5e-08 W=5e-07 +MM16 a_34_4# a_22_6# gnd gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM15 gnd a_34_4# a_31_6# gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM14 a_31_6# clk a_22_6# gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM13 a_22_6# a_2_6# a_17_6# gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM12 a_17_6# D gnd gnd NMOS_VTG L=5e-08 W=2.5e-07 +MM11 Q a_66_6# vdd vdd PMOS_VTG L=5e-08 W=1e-06 +MM9 vdd Q a_76_84# vdd PMOS_VTG L=5e-08 W=2.5e-07 +MM8 a_76_84# clk a_66_6# vdd PMOS_VTG L=5e-08 W=2.5e-07 +MM7 a_66_6# a_2_6# a_61_74# vdd PMOS_VTG L=5e-08 W=5e-07 +MM6 a_61_74# a_34_4# vdd vdd PMOS_VTG L=5e-08 W=5e-07 +MM0 vdd clk a_2_6# vdd PMOS_VTG L=5e-08 W=1e-06 +MM5 a_34_4# a_22_6# vdd vdd PMOS_VTG L=5e-08 W=5e-07 +MM4 vdd a_34_4# a_31_74# vdd PMOS_VTG L=5e-08 W=5e-07 +MM3 a_31_74# a_2_6# a_22_6# vdd PMOS_VTG L=5e-08 W=5e-07 +MM2 a_22_6# clk a_17_74# vdd PMOS_VTG L=5e-08 W=5e-07 +MM1 a_17_74# D vdd vdd PMOS_VTG L=5e-08 W=5e-07 +* c_9 a_66_6# 0 0.271997f +* c_20 clk 0 0.350944f +* c_27 Q 0 0.202617f +* c_32 a_76_84# 0 0.0210573f +* c_38 a_76_6# 0 0.0204911f +* c_45 a_34_4# 0 0.172306f +* c_55 a_2_6# 0 0.283119f +* c_59 a_22_6# 0 0.157312f +* c_64 D 0 0.0816386f +* c_73 gnd 0 0.254131f +* c_81 vdd 0 0.23624f +* +*.include "dff.pex.netlist.dff.pxi" +* +.ends +* +* + +.SUBCKT sram_2_16_1_freepdk45_data_dff ++ din_0 din_1 dout_0 dout_1 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 1 cols: 2 +Xdff_r0_c0 ++ din_0 dout_0 clk vdd gnd ++ dff +Xdff_r0_c1 ++ din_1 dout_1 clk vdd gnd ++ dff +.ENDS sram_2_16_1_freepdk45_data_dff + +* spice ptx M{0} {1} pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p + +* spice ptx M{0} {1} nmos_vtg m=1 w=0.09u l=0.05u pd=0.28u ps=0.28u as=0.01p ad=0.01p + +.SUBCKT sram_2_16_1_freepdk45_pinv_10 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpinv_nmos Z A gnd gnd nmos_vtg m=1 w=0.09u l=0.05u pd=0.28u ps=0.28u as=0.01p ad=0.01p +.ENDS sram_2_16_1_freepdk45_pinv_10 + +.SUBCKT sram_2_16_1_freepdk45_delay_chain ++ in out vdd gnd +* INPUT : in +* OUTPUT: out +* POWER : vdd +* GROUND: gnd +* fanouts: [4, 4, 4, 4, 4, 4, 4, 4, 4] +Xdinv0 ++ in dout_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_0_0 ++ dout_1 n_0_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_0_1 ++ dout_1 n_0_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_0_2 ++ dout_1 n_0_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_0_3 ++ dout_1 n_0_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv1 ++ dout_1 dout_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_1_0 ++ dout_2 n_1_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_1_1 ++ dout_2 n_1_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_1_2 ++ dout_2 n_1_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_1_3 ++ dout_2 n_1_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv2 ++ dout_2 dout_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_2_0 ++ dout_3 n_2_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_2_1 ++ dout_3 n_2_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_2_2 ++ dout_3 n_2_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_2_3 ++ dout_3 n_2_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv3 ++ dout_3 dout_4 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_3_0 ++ dout_4 n_3_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_3_1 ++ dout_4 n_3_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_3_2 ++ dout_4 n_3_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_3_3 ++ dout_4 n_3_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv4 ++ dout_4 dout_5 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_4_0 ++ dout_5 n_4_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_4_1 ++ dout_5 n_4_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_4_2 ++ dout_5 n_4_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_4_3 ++ dout_5 n_4_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv5 ++ dout_5 dout_6 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_5_0 ++ dout_6 n_5_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_5_1 ++ dout_6 n_5_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_5_2 ++ dout_6 n_5_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_5_3 ++ dout_6 n_5_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv6 ++ dout_6 dout_7 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_6_0 ++ dout_7 n_6_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_6_1 ++ dout_7 n_6_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_6_2 ++ dout_7 n_6_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_6_3 ++ dout_7 n_6_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv7 ++ dout_7 dout_8 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_7_0 ++ dout_8 n_7_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_7_1 ++ dout_8 n_7_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_7_2 ++ dout_8 n_7_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_7_3 ++ dout_8 n_7_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdinv8 ++ dout_8 out vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_8_0 ++ out n_8_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_8_1 ++ out n_8_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_8_2 ++ out n_8_2 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +Xdload_8_3 ++ out n_8_3 vdd gnd ++ sram_2_16_1_freepdk45_pinv_10 +.ENDS sram_2_16_1_freepdk45_delay_chain + +* spice ptx M{0} {1} pmos_vtg m=1 w=0.54u l=0.05u pd=1.18u ps=1.18u as=0.07p ad=0.07p + +* spice ptx M{0} {1} nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p + +.SUBCKT sram_2_16_1_freepdk45_pinv_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=1 w=0.54u l=0.05u pd=1.18u ps=1.18u as=0.07p ad=0.07p +Mpinv_nmos Z A gnd gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pinv_0 + +* spice ptx M{0} {1} nmos_vtg m=2 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p + +* spice ptx M{0} {1} pmos_vtg m=2 w=0.54u l=0.05u pd=1.18u ps=1.18u as=0.07p ad=0.07p + +.SUBCKT sram_2_16_1_freepdk45_pinv_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=2 w=0.54u l=0.05u pd=1.18u ps=1.18u as=0.07p ad=0.07p +Mpinv_nmos Z A gnd gnd nmos_vtg m=2 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pinv_1 + +.SUBCKT sram_2_16_1_freepdk45_dff_buf_0 ++ D Q Qb clk vdd gnd +* INPUT : D +* OUTPUT: Q +* OUTPUT: Qb +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* inv1: 2 inv2: 4 +Xdff_buf_dff ++ D qint clk vdd gnd ++ dff +Xdff_buf_inv1 ++ qint Qb vdd gnd ++ sram_2_16_1_freepdk45_pinv_0 +Xdff_buf_inv2 ++ Qb Q vdd gnd ++ sram_2_16_1_freepdk45_pinv_1 +.ENDS sram_2_16_1_freepdk45_dff_buf_0 + +.SUBCKT sram_2_16_1_freepdk45_dff_buf_array ++ din_0 din_1 dout_0 dout_bar_0 dout_1 dout_bar_1 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* OUTPUT: dout_0 +* OUTPUT: dout_bar_0 +* OUTPUT: dout_1 +* OUTPUT: dout_bar_1 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* inv1: 2 inv2: 4 +Xdff_r0_c0 ++ din_0 dout_0 dout_bar_0 clk vdd gnd ++ sram_2_16_1_freepdk45_dff_buf_0 +Xdff_r1_c0 ++ din_1 dout_1 dout_bar_1 clk vdd gnd ++ sram_2_16_1_freepdk45_dff_buf_0 +.ENDS sram_2_16_1_freepdk45_dff_buf_array + +.SUBCKT sram_2_16_1_freepdk45_pinv_3 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpinv_nmos Z A gnd gnd nmos_vtg m=1 w=0.09u l=0.05u pd=0.28u ps=0.28u as=0.01p ad=0.01p +.ENDS sram_2_16_1_freepdk45_pinv_3 + +* spice ptx M{0} {1} pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p + +* spice ptx M{0} {1} nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p + +* spice ptx M{0} {1} nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p + +.SUBCKT sram_2_16_1_freepdk45_pnand2_1 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand2_pmos1 vdd A Z vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand2_pmos2 Z B vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand2_nmos1 Z B net1 gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +Mpnand2_nmos2 net1 A gnd gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pnand2_1 + +* spice ptx M{0} {1} pmos_vtg m=2 w=0.675u l=0.05u pd=1.45u ps=1.45u as=0.08p ad=0.08p + +* spice ptx M{0} {1} nmos_vtg m=2 w=0.225u l=0.05u pd=0.55u ps=0.55u as=0.03p ad=0.03p + +.SUBCKT sram_2_16_1_freepdk45_pinv_7 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=2 w=0.675u l=0.05u pd=1.45u ps=1.45u as=0.08p ad=0.08p +Mpinv_nmos Z A gnd gnd nmos_vtg m=2 w=0.225u l=0.05u pd=0.55u ps=0.55u as=0.03p ad=0.03p +.ENDS sram_2_16_1_freepdk45_pinv_7 + +.SUBCKT sram_2_16_1_freepdk45_pinv_5 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpinv_nmos Z A gnd gnd nmos_vtg m=1 w=0.09u l=0.05u pd=0.28u ps=0.28u as=0.01p ad=0.01p +.ENDS sram_2_16_1_freepdk45_pinv_5 + +.SUBCKT sram_2_16_1_freepdk45_pdriver_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 5] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_freepdk45_pinv_5 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_freepdk45_pinv_7 +.ENDS sram_2_16_1_freepdk45_pdriver_1 + +.SUBCKT sram_2_16_1_freepdk45_pnand2_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand2_pmos1 vdd A Z vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand2_pmos2 Z B vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand2_nmos1 Z B net1 gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +Mpnand2_nmos2 net1 A gnd gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pnand2_0 + +* spice ptx M{0} {1} pmos_vtg m=4 w=0.81u l=0.05u pd=1.72u ps=1.72u as=0.10p ad=0.10p + +* spice ptx M{0} {1} nmos_vtg m=4 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p + +.SUBCKT sram_2_16_1_freepdk45_pinv_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=4 w=0.81u l=0.05u pd=1.72u ps=1.72u as=0.10p ad=0.10p +Mpinv_nmos Z A gnd gnd nmos_vtg m=4 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +.ENDS sram_2_16_1_freepdk45_pinv_2 + +.SUBCKT sram_2_16_1_freepdk45_pdriver ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [12] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_freepdk45_pinv_2 +.ENDS sram_2_16_1_freepdk45_pdriver + +.SUBCKT sram_2_16_1_freepdk45_pand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xpand2_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_freepdk45_pnand2_0 +Xpand2_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_freepdk45_pdriver +.ENDS sram_2_16_1_freepdk45_pand2 + +* spice ptx M{0} {1} pmos_vtg m=5 w=0.81u l=0.05u pd=1.72u ps=1.72u as=0.10p ad=0.10p + +* spice ptx M{0} {1} nmos_vtg m=5 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p + +.SUBCKT sram_2_16_1_freepdk45_pinv_8 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=5 w=0.81u l=0.05u pd=1.72u ps=1.72u as=0.10p ad=0.10p +Mpinv_nmos Z A gnd gnd nmos_vtg m=5 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +.ENDS sram_2_16_1_freepdk45_pinv_8 + +.SUBCKT sram_2_16_1_freepdk45_pinv_6 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=1 w=0.54u l=0.05u pd=1.18u ps=1.18u as=0.07p ad=0.07p +Mpinv_nmos Z A gnd gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pinv_6 + +.SUBCKT sram_2_16_1_freepdk45_pdriver_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 2, 5, 15] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_freepdk45_pinv_5 +Xbuf_inv2 ++ Zb1_int Zb2_int vdd gnd ++ sram_2_16_1_freepdk45_pinv_6 +Xbuf_inv3 ++ Zb2_int Zb3_int vdd gnd ++ sram_2_16_1_freepdk45_pinv_7 +Xbuf_inv4 ++ Zb3_int Z vdd gnd ++ sram_2_16_1_freepdk45_pinv_8 +.ENDS sram_2_16_1_freepdk45_pdriver_0 + +.SUBCKT sram_2_16_1_freepdk45_pdriver_4 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 1] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_freepdk45_pinv_5 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_freepdk45_pinv_5 +.ENDS sram_2_16_1_freepdk45_pdriver_4 + +.SUBCKT sram_2_16_1_freepdk45_pdriver_3 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [2] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_freepdk45_pinv_6 +.ENDS sram_2_16_1_freepdk45_pdriver_3 + +* spice ptx M{0} {1} nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p + +.SUBCKT sram_2_16_1_freepdk45_pnand3_0 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand3_pmos1 vdd A Z vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand3_pmos2 Z B vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand3_pmos3 Z C vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand3_nmos1 Z C net1 gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +Mpnand3_nmos2 net1 B net2 gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +Mpnand3_nmos3 net2 A gnd gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pnand3_0 + +.SUBCKT sram_2_16_1_freepdk45_pand3_0 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_freepdk45_pnand3_0 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_freepdk45_pdriver_3 +.ENDS sram_2_16_1_freepdk45_pand3_0 + +* spice ptx M{0} {1} pmos_vtg m=3 w=0.9u l=0.05u pd=1.90u ps=1.90u as=0.11p ad=0.11p + +* spice ptx M{0} {1} nmos_vtg m=3 w=0.3u l=0.05u pd=0.70u ps=0.70u as=0.04p ad=0.04p + +.SUBCKT sram_2_16_1_freepdk45_pinv_9 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=3 w=0.9u l=0.05u pd=1.90u ps=1.90u as=0.11p ad=0.11p +Mpinv_nmos Z A gnd gnd nmos_vtg m=3 w=0.3u l=0.05u pd=0.70u ps=0.70u as=0.04p ad=0.04p +.ENDS sram_2_16_1_freepdk45_pinv_9 + +.SUBCKT sram_2_16_1_freepdk45_pdriver_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [10] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_freepdk45_pinv_9 +.ENDS sram_2_16_1_freepdk45_pdriver_2 + +.SUBCKT sram_2_16_1_freepdk45_pand3 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_freepdk45_pnand3_0 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_freepdk45_pdriver_2 +.ENDS sram_2_16_1_freepdk45_pand3 + +.SUBCKT sram_2_16_1_freepdk45_control_logic_rw ++ csb web clk rbl_bl s_en w_en p_en_bar wl_en clk_buf vdd gnd +* INPUT : csb +* INPUT : web +* INPUT : clk +* INPUT : rbl_bl +* OUTPUT: s_en +* OUTPUT: w_en +* OUTPUT: p_en_bar +* OUTPUT: wl_en +* OUTPUT: clk_buf +* POWER : vdd +* GROUND: gnd +* word_size 2 +Xctrl_dffs ++ csb web cs_bar cs we_bar we clk_buf vdd gnd ++ sram_2_16_1_freepdk45_dff_buf_array +Xclkbuf ++ clk clk_buf vdd gnd ++ sram_2_16_1_freepdk45_pdriver_0 +Xinv_clk_bar ++ clk_buf clk_bar vdd gnd ++ sram_2_16_1_freepdk45_pinv_3 +Xand2_gated_clk_bar ++ clk_bar cs gated_clk_bar vdd gnd ++ sram_2_16_1_freepdk45_pand2 +Xand2_gated_clk_buf ++ clk_buf cs gated_clk_buf vdd gnd ++ sram_2_16_1_freepdk45_pand2 +Xbuf_wl_en ++ gated_clk_bar wl_en vdd gnd ++ sram_2_16_1_freepdk45_pdriver_1 +Xrbl_bl_delay_inv ++ rbl_bl_delay rbl_bl_delay_bar vdd gnd ++ sram_2_16_1_freepdk45_pinv_3 +Xw_en_and ++ we rbl_bl_delay_bar gated_clk_bar w_en vdd gnd ++ sram_2_16_1_freepdk45_pand3 +Xbuf_s_en_and ++ rbl_bl_delay gated_clk_bar we_bar s_en vdd gnd ++ sram_2_16_1_freepdk45_pand3_0 +Xdelay_chain ++ rbl_bl rbl_bl_delay vdd gnd ++ sram_2_16_1_freepdk45_delay_chain +Xnand_p_en_bar ++ gated_clk_buf rbl_bl_delay p_en_bar_unbuf vdd gnd ++ sram_2_16_1_freepdk45_pnand2_1 +Xbuf_p_en_bar ++ p_en_bar_unbuf p_en_bar vdd gnd ++ sram_2_16_1_freepdk45_pdriver_4 +.ENDS sram_2_16_1_freepdk45_control_logic_rw + +.SUBCKT sram_2_16_1_freepdk45_row_addr_dff ++ din_0 din_1 din_2 din_3 dout_0 dout_1 dout_2 dout_3 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* INPUT : din_3 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* OUTPUT: dout_3 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 4 cols: 1 +Xdff_r0_c0 ++ din_0 dout_0 clk vdd gnd ++ dff +Xdff_r1_c0 ++ din_1 dout_1 clk vdd gnd ++ dff +Xdff_r2_c0 ++ din_2 dout_2 clk vdd gnd ++ dff +Xdff_r3_c0 ++ din_3 dout_3 clk vdd gnd ++ dff +.ENDS sram_2_16_1_freepdk45_row_addr_dff + +.SUBCKT sram_2_16_1_freepdk45_pnand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand2_pmos1 vdd A Z vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand2_pmos2 Z B vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpnand2_nmos1 Z B net1 gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +Mpnand2_nmos2 net1 A gnd gnd nmos_vtg m=1 w=0.18u l=0.05u pd=0.46u ps=0.46u as=0.02p ad=0.02p +.ENDS sram_2_16_1_freepdk45_pnand2 + +.SUBCKT sram_2_16_1_freepdk45_pinv ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mpinv_nmos Z A gnd gnd nmos_vtg m=1 w=0.09u l=0.05u pd=0.28u ps=0.28u as=0.01p ad=0.01p +.ENDS sram_2_16_1_freepdk45_pinv + +.SUBCKT sram_2_16_1_freepdk45_and2_dec_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_freepdk45_pnand2 +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_freepdk45_pinv +.ENDS sram_2_16_1_freepdk45_and2_dec_0 + +.SUBCKT sram_2_16_1_freepdk45_and2_dec ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_freepdk45_pnand2 +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_freepdk45_pinv +.ENDS sram_2_16_1_freepdk45_and2_dec + +.SUBCKT sram_2_16_1_freepdk45_hierarchical_predecode2x4 ++ in_0 in_1 out_0 out_1 out_2 out_3 vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* OUTPUT: out_0 +* OUTPUT: out_1 +* OUTPUT: out_2 +* OUTPUT: out_3 +* POWER : vdd +* GROUND: gnd +Xpre_inv_0 ++ in_0 inbar_0 vdd gnd ++ sram_2_16_1_freepdk45_pinv +Xpre_inv_1 ++ in_1 inbar_1 vdd gnd ++ sram_2_16_1_freepdk45_pinv +XXpre2x4_and_0 ++ inbar_0 inbar_1 out_0 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XXpre2x4_and_1 ++ in_0 inbar_1 out_1 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XXpre2x4_and_2 ++ inbar_0 in_1 out_2 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XXpre2x4_and_3 ++ in_0 in_1 out_3 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +.ENDS sram_2_16_1_freepdk45_hierarchical_predecode2x4 + +.SUBCKT sram_2_16_1_freepdk45_hierarchical_decoder ++ addr_0 addr_1 addr_2 addr_3 decode_0 decode_1 decode_2 decode_3 ++ decode_4 decode_5 decode_6 decode_7 decode_8 decode_9 decode_10 ++ decode_11 decode_12 decode_13 decode_14 decode_15 vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* OUTPUT: decode_0 +* OUTPUT: decode_1 +* OUTPUT: decode_2 +* OUTPUT: decode_3 +* OUTPUT: decode_4 +* OUTPUT: decode_5 +* OUTPUT: decode_6 +* OUTPUT: decode_7 +* OUTPUT: decode_8 +* OUTPUT: decode_9 +* OUTPUT: decode_10 +* OUTPUT: decode_11 +* OUTPUT: decode_12 +* OUTPUT: decode_13 +* OUTPUT: decode_14 +* OUTPUT: decode_15 +* POWER : vdd +* GROUND: gnd +Xpre_0 ++ addr_0 addr_1 out_0 out_1 out_2 out_3 vdd gnd ++ sram_2_16_1_freepdk45_hierarchical_predecode2x4 +Xpre_1 ++ addr_2 addr_3 out_4 out_5 out_6 out_7 vdd gnd ++ sram_2_16_1_freepdk45_hierarchical_predecode2x4 +XDEC_AND_0 ++ out_0 out_4 decode_0 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_4 ++ out_0 out_5 decode_4 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_8 ++ out_0 out_6 decode_8 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_12 ++ out_0 out_7 decode_12 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_1 ++ out_1 out_4 decode_1 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_5 ++ out_1 out_5 decode_5 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_9 ++ out_1 out_6 decode_9 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_13 ++ out_1 out_7 decode_13 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_2 ++ out_2 out_4 decode_2 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_6 ++ out_2 out_5 decode_6 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_10 ++ out_2 out_6 decode_10 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_14 ++ out_2 out_7 decode_14 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_3 ++ out_3 out_4 decode_3 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_7 ++ out_3 out_5 decode_7 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_11 ++ out_3 out_6 decode_11 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +XDEC_AND_15 ++ out_3 out_7 decode_15 vdd gnd ++ sram_2_16_1_freepdk45_and2_dec +.ENDS sram_2_16_1_freepdk45_hierarchical_decoder + +.SUBCKT sram_2_16_1_freepdk45_wordline_driver ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xwld_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_freepdk45_pnand2 +Xwl_driver ++ zb_int Z vdd gnd ++ sram_2_16_1_freepdk45_pinv +.ENDS sram_2_16_1_freepdk45_wordline_driver + +.SUBCKT sram_2_16_1_freepdk45_wordline_driver_array ++ in_0 in_1 in_2 in_3 in_4 in_5 in_6 in_7 in_8 in_9 in_10 in_11 in_12 ++ in_13 in_14 in_15 wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 wl_7 wl_8 wl_9 ++ wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 en vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* INPUT : in_2 +* INPUT : in_3 +* INPUT : in_4 +* INPUT : in_5 +* INPUT : in_6 +* INPUT : in_7 +* INPUT : in_8 +* INPUT : in_9 +* INPUT : in_10 +* INPUT : in_11 +* INPUT : in_12 +* INPUT : in_13 +* INPUT : in_14 +* INPUT : in_15 +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* rows: 16 cols: 2 +Xwl_driver_and0 ++ in_0 en wl_0 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and1 ++ in_1 en wl_1 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and2 ++ in_2 en wl_2 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and3 ++ in_3 en wl_3 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and4 ++ in_4 en wl_4 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and5 ++ in_5 en wl_5 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and6 ++ in_6 en wl_6 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and7 ++ in_7 en wl_7 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and8 ++ in_8 en wl_8 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and9 ++ in_9 en wl_9 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and10 ++ in_10 en wl_10 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and11 ++ in_11 en wl_11 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and12 ++ in_12 en wl_12 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and13 ++ in_13 en wl_13 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and14 ++ in_14 en wl_14 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +Xwl_driver_and15 ++ in_15 en wl_15 vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver +.ENDS sram_2_16_1_freepdk45_wordline_driver_array + +.SUBCKT sram_2_16_1_freepdk45_port_address ++ addr_0 addr_1 addr_2 addr_3 wl_en wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 ++ wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 rbl_wl vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* INPUT : wl_en +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* OUTPUT: rbl_wl +* POWER : vdd +* GROUND: gnd +Xrow_decoder ++ addr_0 addr_1 addr_2 addr_3 dec_out_0 dec_out_1 dec_out_2 dec_out_3 ++ dec_out_4 dec_out_5 dec_out_6 dec_out_7 dec_out_8 dec_out_9 dec_out_10 ++ dec_out_11 dec_out_12 dec_out_13 dec_out_14 dec_out_15 vdd gnd ++ sram_2_16_1_freepdk45_hierarchical_decoder +Xwordline_driver ++ dec_out_0 dec_out_1 dec_out_2 dec_out_3 dec_out_4 dec_out_5 dec_out_6 ++ dec_out_7 dec_out_8 dec_out_9 dec_out_10 dec_out_11 dec_out_12 ++ dec_out_13 dec_out_14 dec_out_15 wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 ++ wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_en vdd gnd ++ sram_2_16_1_freepdk45_wordline_driver_array +Xrbl_driver ++ wl_en vdd rbl_wl vdd gnd ++ sram_2_16_1_freepdk45_and2_dec_0 +.ENDS sram_2_16_1_freepdk45_port_address + +.SUBCKT write_driver din bl br en vdd gnd +*inverters for enable and data input +minP bl_bar din vdd vdd pmos_vtg w=360.000000n l=50.000000n +minN bl_bar din gnd gnd nmos_vtg w=180.000000n l=50.000000n +moutP en_bar en vdd vdd pmos_vtg w=360.000000n l=50.000000n +moutN en_bar en gnd gnd nmos_vtg w=180.000000n l=50.000000n + +*tristate for BL +mout0P int1 bl_bar vdd vdd pmos_vtg w=360.000000n l=50.000000n +mout0P2 bl en_bar int1 vdd pmos_vtg w=360.000000n l=50.000000n +mout0N bl en int2 gnd nmos_vtg w=180.000000n l=50.000000n +mout0N2 int2 bl_bar gnd gnd nmos_vtg w=180.000000n l=50.000000n + +*tristate for BR +mout1P int3 din vdd vdd pmos_vtg w=360.000000n l=50.000000n +mout1P2 br en_bar int3 vdd pmos_vtg w=360.000000n l=50.000000n +mout1N br en int4 gnd nmos_vtg w=180.000000n l=50.000000n +mout1N2 int4 din gnd gnd nmos_vtg w=180.000000n l=50.000000n +.ENDS write_driver + + +.SUBCKT sram_2_16_1_freepdk45_write_driver_array ++ data_0 data_1 bl_0 br_0 bl_1 br_1 en vdd gnd +* INPUT : data_0 +* INPUT : data_1 +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* word_size 2 +Xwrite_driver0 ++ data_0 bl_0 br_0 en vdd gnd ++ write_driver +Xwrite_driver1 ++ data_1 bl_1 br_1 en vdd gnd ++ write_driver +.ENDS sram_2_16_1_freepdk45_write_driver_array + +.SUBCKT sense_amp bl br dout en vdd gnd +M_1 dint net_1 vdd vdd pmos_vtg w=540.0n l=50.0n +M_3 net_1 dint vdd vdd pmos_vtg w=540.0n l=50.0n +M_2 dint net_1 net_2 gnd nmos_vtg w=270.0n l=50.0n +M_8 net_1 dint net_2 gnd nmos_vtg w=270.0n l=50.0n +M_5 bl en dint vdd pmos_vtg w=720.0n l=50.0n +M_6 br en net_1 vdd pmos_vtg w=720.0n l=50.0n +M_7 net_2 en gnd gnd nmos_vtg w=270.0n l=50.0n + +M_9 dout_bar dint vdd vdd pmos_vtg w=180.0n l=50.0n +M_10 dout_bar dint gnd gnd nmos_vtg w=90.0n l=50.0n +M_11 dout dout_bar vdd vdd pmos_vtg w=540.0n l=50.0n +M_12 dout dout_bar gnd gnd nmos_vtg w=270.0n l=50.0n +.ENDS sense_amp + + +.SUBCKT sram_2_16_1_freepdk45_sense_amp_array ++ data_0 bl_0 br_0 data_1 bl_1 br_1 en vdd gnd +* OUTPUT: data_0 +* INPUT : bl_0 +* INPUT : br_0 +* OUTPUT: data_1 +* INPUT : bl_1 +* INPUT : br_1 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* words_per_row: 1 +Xsa_d0 ++ bl_0 br_0 data_0 en vdd gnd ++ sense_amp +Xsa_d1 ++ bl_1 br_1 data_1 en vdd gnd ++ sense_amp +.ENDS sram_2_16_1_freepdk45_sense_amp_array + +.SUBCKT sram_2_16_1_freepdk45_precharge_0 ++ bl br en_bar vdd +* OUTPUT: bl +* OUTPUT: br +* INPUT : en_bar +* POWER : vdd +Mlower_pmos bl en_bar br vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mupper_pmos1 bl en_bar vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +Mupper_pmos2 br en_bar vdd vdd pmos_vtg m=1 w=0.27u l=0.05u pd=0.64u ps=0.64u as=0.03p ad=0.03p +.ENDS sram_2_16_1_freepdk45_precharge_0 + +.SUBCKT sram_2_16_1_freepdk45_precharge_array ++ bl_0 br_0 bl_1 br_1 bl_2 br_2 en_bar vdd +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* OUTPUT: bl_2 +* OUTPUT: br_2 +* INPUT : en_bar +* POWER : vdd +* cols: 3 size: 1 bl: bl br: br +Xpre_column_0 ++ bl_0 br_0 en_bar vdd ++ sram_2_16_1_freepdk45_precharge_0 +Xpre_column_1 ++ bl_1 br_1 en_bar vdd ++ sram_2_16_1_freepdk45_precharge_0 +Xpre_column_2 ++ bl_2 br_2 en_bar vdd ++ sram_2_16_1_freepdk45_precharge_0 +.ENDS sram_2_16_1_freepdk45_precharge_array + +.SUBCKT sram_2_16_1_freepdk45_port_data ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 dout_0 dout_1 din_0 din_1 s_en ++ p_en_bar w_en vdd gnd +* INOUT : rbl_bl +* INOUT : rbl_br +* INOUT : bl_0 +* INOUT : br_0 +* INOUT : bl_1 +* INOUT : br_1 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* INPUT : din_0 +* INPUT : din_1 +* INPUT : s_en +* INPUT : p_en_bar +* INPUT : w_en +* POWER : vdd +* GROUND: gnd +Xprecharge_array0 ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 p_en_bar vdd ++ sram_2_16_1_freepdk45_precharge_array +Xsense_amp_array0 ++ dout_0 bl_0 br_0 dout_1 bl_1 br_1 s_en vdd gnd ++ sram_2_16_1_freepdk45_sense_amp_array +Xwrite_driver_array0 ++ din_0 din_1 bl_0 br_0 bl_1 br_1 w_en vdd gnd ++ sram_2_16_1_freepdk45_write_driver_array +.ENDS sram_2_16_1_freepdk45_port_data + +.SUBCKT dummy_cell_1rw bl br wl vdd gnd +* Inverter 1 +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors +MM3 bl_noconn wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br_noconn wl Q_bar gnd NMOS_VTG W=135.00n L=50n +.ENDS dummy_cell_1rw + + +.SUBCKT sram_2_16_1_freepdk45_dummy_array_3 ++ bl_0_0 br_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ wl_0_17 wl_0_18 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* INPUT : wl_0_17 +* INPUT : wl_0_18 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r1_c0 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ dummy_cell_1rw +Xbit_r2_c0 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ dummy_cell_1rw +Xbit_r3_c0 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ dummy_cell_1rw +Xbit_r4_c0 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ dummy_cell_1rw +Xbit_r5_c0 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ dummy_cell_1rw +Xbit_r6_c0 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ dummy_cell_1rw +Xbit_r7_c0 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ dummy_cell_1rw +Xbit_r8_c0 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ dummy_cell_1rw +Xbit_r9_c0 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ dummy_cell_1rw +Xbit_r10_c0 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ dummy_cell_1rw +Xbit_r11_c0 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ dummy_cell_1rw +Xbit_r12_c0 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ dummy_cell_1rw +Xbit_r13_c0 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ dummy_cell_1rw +Xbit_r14_c0 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ dummy_cell_1rw +Xbit_r15_c0 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ dummy_cell_1rw +Xbit_r16_c0 ++ bl_0_0 br_0_0 wl_0_16 vdd gnd ++ dummy_cell_1rw +Xbit_r17_c0 ++ bl_0_0 br_0_0 wl_0_17 vdd gnd ++ dummy_cell_1rw +Xbit_r18_c0 ++ bl_0_0 br_0_0 wl_0_18 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_freepdk45_dummy_array_3 + +.SUBCKT cell_1rw bl br wl vdd gnd +* Inverter 1 +MM0 Q_bar Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 Q_bar Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q Q_bar gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q Q_bar vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors +MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br wl Q_bar gnd NMOS_VTG W=135.00n L=50n +.ENDS cell_1rw + + +.SUBCKT sram_2_16_1_freepdk45_bitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 ++ wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 ++ wl_0_15 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* POWER : vdd +* GROUND: gnd +* rows: 16 cols: 2 +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ cell_1rw +Xbit_r1_c0 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ cell_1rw +Xbit_r2_c0 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ cell_1rw +Xbit_r3_c0 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ cell_1rw +Xbit_r4_c0 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ cell_1rw +Xbit_r5_c0 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ cell_1rw +Xbit_r6_c0 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ cell_1rw +Xbit_r7_c0 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ cell_1rw +Xbit_r8_c0 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ cell_1rw +Xbit_r9_c0 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ cell_1rw +Xbit_r10_c0 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ cell_1rw +Xbit_r11_c0 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ cell_1rw +Xbit_r12_c0 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ cell_1rw +Xbit_r13_c0 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ cell_1rw +Xbit_r14_c0 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ cell_1rw +Xbit_r15_c0 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ cell_1rw +Xbit_r1_c1 ++ bl_0_1 br_0_1 wl_0_1 vdd gnd ++ cell_1rw +Xbit_r2_c1 ++ bl_0_1 br_0_1 wl_0_2 vdd gnd ++ cell_1rw +Xbit_r3_c1 ++ bl_0_1 br_0_1 wl_0_3 vdd gnd ++ cell_1rw +Xbit_r4_c1 ++ bl_0_1 br_0_1 wl_0_4 vdd gnd ++ cell_1rw +Xbit_r5_c1 ++ bl_0_1 br_0_1 wl_0_5 vdd gnd ++ cell_1rw +Xbit_r6_c1 ++ bl_0_1 br_0_1 wl_0_6 vdd gnd ++ cell_1rw +Xbit_r7_c1 ++ bl_0_1 br_0_1 wl_0_7 vdd gnd ++ cell_1rw +Xbit_r8_c1 ++ bl_0_1 br_0_1 wl_0_8 vdd gnd ++ cell_1rw +Xbit_r9_c1 ++ bl_0_1 br_0_1 wl_0_9 vdd gnd ++ cell_1rw +Xbit_r10_c1 ++ bl_0_1 br_0_1 wl_0_10 vdd gnd ++ cell_1rw +Xbit_r11_c1 ++ bl_0_1 br_0_1 wl_0_11 vdd gnd ++ cell_1rw +Xbit_r12_c1 ++ bl_0_1 br_0_1 wl_0_12 vdd gnd ++ cell_1rw +Xbit_r13_c1 ++ bl_0_1 br_0_1 wl_0_13 vdd gnd ++ cell_1rw +Xbit_r14_c1 ++ bl_0_1 br_0_1 wl_0_14 vdd gnd ++ cell_1rw +Xbit_r15_c1 ++ bl_0_1 br_0_1 wl_0_15 vdd gnd ++ cell_1rw +.ENDS sram_2_16_1_freepdk45_bitcell_array + +.SUBCKT sram_2_16_1_freepdk45_dummy_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_freepdk45_dummy_array + +.SUBCKT replica_cell_1rw bl br wl vdd gnd +* Inverter 1 +MM0 vdd Q gnd gnd NMOS_VTG W=205.00n L=50n +MM4 vdd Q vdd vdd PMOS_VTG W=90n L=50n + +* Inverer 2 +MM1 Q vdd gnd gnd NMOS_VTG W=205.00n L=50n +MM5 Q vdd vdd vdd PMOS_VTG W=90n L=50n + +* Access transistors +MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n +MM2 br wl vdd gnd NMOS_VTG W=135.00n L=50n +.ENDS cell_1rw + + +.SUBCKT sram_2_16_1_freepdk45_replica_column ++ bl_0_0 br_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd +* OUTPUT: bl_0_0 +* OUTPUT: br_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +Xrbc_0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ replica_cell_1rw +Xrbc_1 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ replica_cell_1rw +Xrbc_2 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ replica_cell_1rw +Xrbc_3 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ replica_cell_1rw +Xrbc_4 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ replica_cell_1rw +Xrbc_5 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ replica_cell_1rw +Xrbc_6 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ replica_cell_1rw +Xrbc_7 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ replica_cell_1rw +Xrbc_8 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ replica_cell_1rw +Xrbc_9 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ replica_cell_1rw +Xrbc_10 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ replica_cell_1rw +Xrbc_11 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ replica_cell_1rw +Xrbc_12 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ replica_cell_1rw +Xrbc_13 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ replica_cell_1rw +Xrbc_14 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ replica_cell_1rw +Xrbc_15 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ replica_cell_1rw +Xrbc_16 ++ bl_0_0 br_0_0 wl_0_16 vdd gnd ++ replica_cell_1rw +.ENDS sram_2_16_1_freepdk45_replica_column + +.SUBCKT sram_2_16_1_freepdk45_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* POWER : vdd +* GROUND: gnd +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xbitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 ++ wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 ++ wl_0_15 vdd gnd ++ sram_2_16_1_freepdk45_bitcell_array +Xreplica_col_0 ++ rbl_bl_0_0 rbl_br_0_0 rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 ++ wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 ++ wl_0_14 wl_0_15 vdd gnd ++ sram_2_16_1_freepdk45_replica_column +Xdummy_row_0 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 vdd gnd ++ sram_2_16_1_freepdk45_dummy_array +.ENDS sram_2_16_1_freepdk45_replica_bitcell_array + +.SUBCKT sram_2_16_1_freepdk45_dummy_array_1 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c2 ++ bl_0_2 br_0_2 wl_0_0 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_freepdk45_dummy_array_1 + +.SUBCKT sram_2_16_1_freepdk45_dummy_array_0 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c2 ++ bl_0_2 br_0_2 wl_0_0 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_freepdk45_dummy_array_0 + +.SUBCKT sram_2_16_1_freepdk45_dummy_array_2 ++ bl_0_0 br_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ wl_0_17 wl_0_18 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* INPUT : wl_0_17 +* INPUT : wl_0_18 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r1_c0 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ dummy_cell_1rw +Xbit_r2_c0 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ dummy_cell_1rw +Xbit_r3_c0 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ dummy_cell_1rw +Xbit_r4_c0 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ dummy_cell_1rw +Xbit_r5_c0 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ dummy_cell_1rw +Xbit_r6_c0 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ dummy_cell_1rw +Xbit_r7_c0 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ dummy_cell_1rw +Xbit_r8_c0 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ dummy_cell_1rw +Xbit_r9_c0 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ dummy_cell_1rw +Xbit_r10_c0 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ dummy_cell_1rw +Xbit_r11_c0 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ dummy_cell_1rw +Xbit_r12_c0 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ dummy_cell_1rw +Xbit_r13_c0 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ dummy_cell_1rw +Xbit_r14_c0 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ dummy_cell_1rw +Xbit_r15_c0 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ dummy_cell_1rw +Xbit_r16_c0 ++ bl_0_0 br_0_0 wl_0_16 vdd gnd ++ dummy_cell_1rw +Xbit_r17_c0 ++ bl_0_0 br_0_0 wl_0_17 vdd gnd ++ dummy_cell_1rw +Xbit_r18_c0 ++ bl_0_0 br_0_0 wl_0_18 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_freepdk45_dummy_array_2 + +.SUBCKT sram_2_16_1_freepdk45_capped_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* POWER : vdd +* GROUND: gnd +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xreplica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd ++ sram_2_16_1_freepdk45_replica_bitcell_array +Xdummy_row_bot ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 gnd vdd gnd ++ sram_2_16_1_freepdk45_dummy_array_1 +Xdummy_row_top ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 gnd vdd gnd ++ sram_2_16_1_freepdk45_dummy_array_0 +Xdummy_col_left ++ dummy_left_bl_0_0 dummy_left_br_0_0 gnd rbl_wl_0_0 wl_0_0 wl_0_1 ++ wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 gnd vdd gnd ++ sram_2_16_1_freepdk45_dummy_array_2 +Xdummy_col_right ++ dummy_right_bl_0_0 dummy_right_br_0_0 gnd rbl_wl_0_0 wl_0_0 wl_0_1 ++ wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 gnd vdd gnd ++ sram_2_16_1_freepdk45_dummy_array_3 +.ENDS sram_2_16_1_freepdk45_capped_replica_bitcell_array + +.SUBCKT sram_2_16_1_freepdk45_bank ++ dout0_0 dout0_1 rbl_bl_0_0 din0_0 din0_1 addr0_0 addr0_1 addr0_2 ++ addr0_3 s_en0 p_en_bar0 w_en0 wl_en0 vdd gnd +* OUTPUT: dout0_0 +* OUTPUT: dout0_1 +* OUTPUT: rbl_bl_0_0 +* INPUT : din0_0 +* INPUT : din0_1 +* INPUT : addr0_0 +* INPUT : addr0_1 +* INPUT : addr0_2 +* INPUT : addr0_3 +* INPUT : s_en0 +* INPUT : p_en_bar0 +* INPUT : w_en0 +* INPUT : wl_en0 +* POWER : vdd +* GROUND: gnd +Xbitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd ++ sram_2_16_1_freepdk45_capped_replica_bitcell_array +Xport_data0 ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 dout0_0 dout0_1 ++ din0_0 din0_1 s_en0 p_en_bar0 w_en0 vdd gnd ++ sram_2_16_1_freepdk45_port_data +Xport_address0 ++ addr0_0 addr0_1 addr0_2 addr0_3 wl_en0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 ++ wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 ++ wl_0_13 wl_0_14 wl_0_15 rbl_wl0 vdd gnd ++ sram_2_16_1_freepdk45_port_address +.ENDS sram_2_16_1_freepdk45_bank + +.SUBCKT sram_2_16_1_freepdk45 ++ din0[0] din0[1] addr0[0] addr0[1] addr0[2] addr0[3] csb0 web0 clk0 ++ dout0[0] dout0[1] vdd gnd +* INPUT : din0[0] +* INPUT : din0[1] +* INPUT : addr0[0] +* INPUT : addr0[1] +* INPUT : addr0[2] +* INPUT : addr0[3] +* INPUT : csb0 +* INPUT : web0 +* INPUT : clk0 +* OUTPUT: dout0[0] +* OUTPUT: dout0[1] +* POWER : vdd +* GROUND: gnd +Xbank0 ++ dout0[0] dout0[1] rbl_bl0 bank_din0_0 bank_din0_1 a0_0 a0_1 a0_2 a0_3 ++ s_en0 p_en_bar0 w_en0 wl_en0 vdd gnd ++ sram_2_16_1_freepdk45_bank +Xcontrol0 ++ csb0 web0 clk0 rbl_bl0 s_en0 w_en0 p_en_bar0 wl_en0 clk_buf0 vdd gnd ++ sram_2_16_1_freepdk45_control_logic_rw +Xrow_address0 ++ addr0[0] addr0[1] addr0[2] addr0[3] a0_0 a0_1 a0_2 a0_3 clk_buf0 vdd ++ gnd ++ sram_2_16_1_freepdk45_row_addr_dff +Xdata_dff0 ++ din0[0] din0[1] bank_din0_0 bank_din0_1 clk_buf0 vdd gnd ++ sram_2_16_1_freepdk45_data_dff +.ENDS sram_2_16_1_freepdk45 diff --git a/compiler/tests/sp_files/sram_2_16_1_scn4m_subm.sp b/compiler/tests/sp_files/sram_2_16_1_scn4m_subm.sp new file mode 100644 index 00000000..c6836a9d --- /dev/null +++ b/compiler/tests/sp_files/sram_2_16_1_scn4m_subm.sp @@ -0,0 +1,1758 @@ +************************************************** +* OpenRAM generated memory. +* Words: 16 +* Data bits: 2 +* Banks: 1 +* Column mux: 1:1 +* Trimmed: False +* LVS: False +************************************************** + +*********************** "cell_1rw" ****************************** +.SUBCKT replica_cell_1rw bl br wl vdd gnd +* SPICE3 file created from cell_1rw.ext - technology: scmos + +* Inverter 1 +M1000 Q vdd vdd vdd p w=0.6u l=0.8u +M1002 Q vdd gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q vdd vdd p w=0.6u l=0.8u +M1003 gnd Q vdd gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl gnd n w=0.8u l=0.4u +M1005 vdd wl br gnd n w=0.8u l=0.4u + +.ENDS + +.SUBCKT sram_2_16_1_scn4m_subm_replica_column ++ bl_0_0 br_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd +* OUTPUT: bl_0_0 +* OUTPUT: br_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +Xrbc_0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ replica_cell_1rw +Xrbc_1 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ replica_cell_1rw +Xrbc_2 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ replica_cell_1rw +Xrbc_3 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ replica_cell_1rw +Xrbc_4 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ replica_cell_1rw +Xrbc_5 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ replica_cell_1rw +Xrbc_6 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ replica_cell_1rw +Xrbc_7 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ replica_cell_1rw +Xrbc_8 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ replica_cell_1rw +Xrbc_9 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ replica_cell_1rw +Xrbc_10 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ replica_cell_1rw +Xrbc_11 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ replica_cell_1rw +Xrbc_12 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ replica_cell_1rw +Xrbc_13 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ replica_cell_1rw +Xrbc_14 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ replica_cell_1rw +Xrbc_15 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ replica_cell_1rw +Xrbc_16 ++ bl_0_0 br_0_0 wl_0_16 vdd gnd ++ replica_cell_1rw +.ENDS sram_2_16_1_scn4m_subm_replica_column + +*********************** "cell_1rw" ****************************** +.SUBCKT cell_1rw bl br wl vdd gnd +* SPICE3 file created from cell_1rw.ext - technology: scmos + +* Inverter 1 +M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u +M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u +M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl gnd n w=0.8u l=0.4u +M1005 Q_bar wl br gnd n w=0.8u l=0.4u + +.ENDS + +.SUBCKT sram_2_16_1_scn4m_subm_bitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 ++ wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 ++ wl_0_15 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* POWER : vdd +* GROUND: gnd +* rows: 16 cols: 2 +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ cell_1rw +Xbit_r1_c0 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ cell_1rw +Xbit_r2_c0 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ cell_1rw +Xbit_r3_c0 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ cell_1rw +Xbit_r4_c0 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ cell_1rw +Xbit_r5_c0 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ cell_1rw +Xbit_r6_c0 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ cell_1rw +Xbit_r7_c0 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ cell_1rw +Xbit_r8_c0 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ cell_1rw +Xbit_r9_c0 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ cell_1rw +Xbit_r10_c0 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ cell_1rw +Xbit_r11_c0 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ cell_1rw +Xbit_r12_c0 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ cell_1rw +Xbit_r13_c0 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ cell_1rw +Xbit_r14_c0 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ cell_1rw +Xbit_r15_c0 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ cell_1rw +Xbit_r1_c1 ++ bl_0_1 br_0_1 wl_0_1 vdd gnd ++ cell_1rw +Xbit_r2_c1 ++ bl_0_1 br_0_1 wl_0_2 vdd gnd ++ cell_1rw +Xbit_r3_c1 ++ bl_0_1 br_0_1 wl_0_3 vdd gnd ++ cell_1rw +Xbit_r4_c1 ++ bl_0_1 br_0_1 wl_0_4 vdd gnd ++ cell_1rw +Xbit_r5_c1 ++ bl_0_1 br_0_1 wl_0_5 vdd gnd ++ cell_1rw +Xbit_r6_c1 ++ bl_0_1 br_0_1 wl_0_6 vdd gnd ++ cell_1rw +Xbit_r7_c1 ++ bl_0_1 br_0_1 wl_0_7 vdd gnd ++ cell_1rw +Xbit_r8_c1 ++ bl_0_1 br_0_1 wl_0_8 vdd gnd ++ cell_1rw +Xbit_r9_c1 ++ bl_0_1 br_0_1 wl_0_9 vdd gnd ++ cell_1rw +Xbit_r10_c1 ++ bl_0_1 br_0_1 wl_0_10 vdd gnd ++ cell_1rw +Xbit_r11_c1 ++ bl_0_1 br_0_1 wl_0_11 vdd gnd ++ cell_1rw +Xbit_r12_c1 ++ bl_0_1 br_0_1 wl_0_12 vdd gnd ++ cell_1rw +Xbit_r13_c1 ++ bl_0_1 br_0_1 wl_0_13 vdd gnd ++ cell_1rw +Xbit_r14_c1 ++ bl_0_1 br_0_1 wl_0_14 vdd gnd ++ cell_1rw +Xbit_r15_c1 ++ bl_0_1 br_0_1 wl_0_15 vdd gnd ++ cell_1rw +.ENDS sram_2_16_1_scn4m_subm_bitcell_array + +*********************** "dummy_cell_1rw" ****************************** +.SUBCKT dummy_cell_1rw bl br wl vdd gnd + +* Inverter 1 +M1000 Q Q_bar vdd vdd p w=0.6u l=0.8u +M1002 Q Q_bar gnd gnd n w=1.6u l=0.4u + +* Inverter 2 +M1001 vdd Q Q_bar vdd p w=0.6u l=0.8u +M1003 gnd Q Q_bar gnd n w=1.6u l=0.4u + +* Access transistors +M1004 Q wl bl_noconn gnd n w=0.8u l=0.4u +M1005 Q_bar wl br_noconn gnd n w=0.8u l=0.4u + +.ENDS + +.SUBCKT sram_2_16_1_scn4m_subm_dummy_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_scn4m_subm_dummy_array + +.SUBCKT sram_2_16_1_scn4m_subm_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* POWER : vdd +* GROUND: gnd +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xbitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 ++ wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 ++ wl_0_15 vdd gnd ++ sram_2_16_1_scn4m_subm_bitcell_array +Xreplica_col_0 ++ rbl_bl_0_0 rbl_br_0_0 rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 ++ wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 ++ wl_0_14 wl_0_15 vdd gnd ++ sram_2_16_1_scn4m_subm_replica_column +Xdummy_row_0 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 vdd gnd ++ sram_2_16_1_scn4m_subm_dummy_array +.ENDS sram_2_16_1_scn4m_subm_replica_bitcell_array + +.SUBCKT sram_2_16_1_scn4m_subm_dummy_array_2 ++ bl_0_0 br_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ wl_0_17 wl_0_18 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* INPUT : wl_0_17 +* INPUT : wl_0_18 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r1_c0 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ dummy_cell_1rw +Xbit_r2_c0 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ dummy_cell_1rw +Xbit_r3_c0 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ dummy_cell_1rw +Xbit_r4_c0 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ dummy_cell_1rw +Xbit_r5_c0 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ dummy_cell_1rw +Xbit_r6_c0 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ dummy_cell_1rw +Xbit_r7_c0 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ dummy_cell_1rw +Xbit_r8_c0 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ dummy_cell_1rw +Xbit_r9_c0 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ dummy_cell_1rw +Xbit_r10_c0 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ dummy_cell_1rw +Xbit_r11_c0 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ dummy_cell_1rw +Xbit_r12_c0 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ dummy_cell_1rw +Xbit_r13_c0 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ dummy_cell_1rw +Xbit_r14_c0 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ dummy_cell_1rw +Xbit_r15_c0 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ dummy_cell_1rw +Xbit_r16_c0 ++ bl_0_0 br_0_0 wl_0_16 vdd gnd ++ dummy_cell_1rw +Xbit_r17_c0 ++ bl_0_0 br_0_0 wl_0_17 vdd gnd ++ dummy_cell_1rw +Xbit_r18_c0 ++ bl_0_0 br_0_0 wl_0_18 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_scn4m_subm_dummy_array_2 + +.SUBCKT sram_2_16_1_scn4m_subm_dummy_array_0 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c2 ++ bl_0_2 br_0_2 wl_0_0 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_scn4m_subm_dummy_array_0 + +.SUBCKT sram_2_16_1_scn4m_subm_dummy_array_1 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c1 ++ bl_0_1 br_0_1 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r0_c2 ++ bl_0_2 br_0_2 wl_0_0 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_scn4m_subm_dummy_array_1 + +.SUBCKT sram_2_16_1_scn4m_subm_dummy_array_3 ++ bl_0_0 br_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ wl_0_17 wl_0_18 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* INPUT : wl_0_17 +* INPUT : wl_0_18 +* POWER : vdd +* GROUND: gnd +Xbit_r0_c0 ++ bl_0_0 br_0_0 wl_0_0 vdd gnd ++ dummy_cell_1rw +Xbit_r1_c0 ++ bl_0_0 br_0_0 wl_0_1 vdd gnd ++ dummy_cell_1rw +Xbit_r2_c0 ++ bl_0_0 br_0_0 wl_0_2 vdd gnd ++ dummy_cell_1rw +Xbit_r3_c0 ++ bl_0_0 br_0_0 wl_0_3 vdd gnd ++ dummy_cell_1rw +Xbit_r4_c0 ++ bl_0_0 br_0_0 wl_0_4 vdd gnd ++ dummy_cell_1rw +Xbit_r5_c0 ++ bl_0_0 br_0_0 wl_0_5 vdd gnd ++ dummy_cell_1rw +Xbit_r6_c0 ++ bl_0_0 br_0_0 wl_0_6 vdd gnd ++ dummy_cell_1rw +Xbit_r7_c0 ++ bl_0_0 br_0_0 wl_0_7 vdd gnd ++ dummy_cell_1rw +Xbit_r8_c0 ++ bl_0_0 br_0_0 wl_0_8 vdd gnd ++ dummy_cell_1rw +Xbit_r9_c0 ++ bl_0_0 br_0_0 wl_0_9 vdd gnd ++ dummy_cell_1rw +Xbit_r10_c0 ++ bl_0_0 br_0_0 wl_0_10 vdd gnd ++ dummy_cell_1rw +Xbit_r11_c0 ++ bl_0_0 br_0_0 wl_0_11 vdd gnd ++ dummy_cell_1rw +Xbit_r12_c0 ++ bl_0_0 br_0_0 wl_0_12 vdd gnd ++ dummy_cell_1rw +Xbit_r13_c0 ++ bl_0_0 br_0_0 wl_0_13 vdd gnd ++ dummy_cell_1rw +Xbit_r14_c0 ++ bl_0_0 br_0_0 wl_0_14 vdd gnd ++ dummy_cell_1rw +Xbit_r15_c0 ++ bl_0_0 br_0_0 wl_0_15 vdd gnd ++ dummy_cell_1rw +Xbit_r16_c0 ++ bl_0_0 br_0_0 wl_0_16 vdd gnd ++ dummy_cell_1rw +Xbit_r17_c0 ++ bl_0_0 br_0_0 wl_0_17 vdd gnd ++ dummy_cell_1rw +Xbit_r18_c0 ++ bl_0_0 br_0_0 wl_0_18 vdd gnd ++ dummy_cell_1rw +.ENDS sram_2_16_1_scn4m_subm_dummy_array_3 + +.SUBCKT sram_2_16_1_scn4m_subm_capped_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* POWER : vdd +* GROUND: gnd +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xreplica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl_0_0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd ++ sram_2_16_1_scn4m_subm_replica_bitcell_array +Xdummy_row_bot ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 gnd vdd gnd ++ sram_2_16_1_scn4m_subm_dummy_array_1 +Xdummy_row_top ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 gnd vdd gnd ++ sram_2_16_1_scn4m_subm_dummy_array_0 +Xdummy_col_left ++ dummy_left_bl_0_0 dummy_left_br_0_0 gnd rbl_wl_0_0 wl_0_0 wl_0_1 ++ wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 gnd vdd gnd ++ sram_2_16_1_scn4m_subm_dummy_array_2 +Xdummy_col_right ++ dummy_right_bl_0_0 dummy_right_br_0_0 gnd rbl_wl_0_0 wl_0_0 wl_0_1 ++ wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 gnd vdd gnd ++ sram_2_16_1_scn4m_subm_dummy_array_3 +.ENDS sram_2_16_1_scn4m_subm_capped_replica_bitcell_array +*********************** "sense_amp" ****************************** + +.SUBCKT sense_amp bl br dout en vdd gnd + +* SPICE3 file created from sense_amp.ext - technology: scmos + +M1000 gnd en a_56_432# gnd n w=1.8u l=0.4u +M1001 a_56_432# a_48_304# dint gnd n w=1.8u l=0.4u +M1002 a_48_304# dint a_56_432# gnd n w=1.8u l=0.4u +M1003 vdd a_48_304# dint vdd p w=3.6u l=0.4u +M1004 a_48_304# dint vdd vdd p w=3.6u l=0.4u +M1005 bl en dint vdd p w=4.8u l=0.4u +M1006 a_48_304# en br vdd p w=4.8u l=0.4u + +M1007 dout_bar dint vdd vdd p w=1.6u l=0.4u +M1008 gnd dint dout_bar gnd n w=0.8u l=0.4u +M1009 dout dout_bar vdd vdd p w=4.8u l=0.4u +M1010 gnd dout_bar dout gnd n w=2.4u l=0.4u +.ENDS + +.SUBCKT sram_2_16_1_scn4m_subm_sense_amp_array ++ data_0 bl_0 br_0 data_1 bl_1 br_1 en vdd gnd +* OUTPUT: data_0 +* INPUT : bl_0 +* INPUT : br_0 +* OUTPUT: data_1 +* INPUT : bl_1 +* INPUT : br_1 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* words_per_row: 1 +Xsa_d0 ++ bl_0 br_0 data_0 en vdd gnd ++ sense_amp +Xsa_d1 ++ bl_1 br_1 data_1 en vdd gnd ++ sense_amp +.ENDS sram_2_16_1_scn4m_subm_sense_amp_array + +* spice ptx M{0} {1} p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p + +.SUBCKT sram_2_16_1_scn4m_subm_precharge_0 ++ bl br en_bar vdd +* OUTPUT: bl +* OUTPUT: br +* INPUT : en_bar +* POWER : vdd +Mlower_pmos bl en_bar br vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mupper_pmos1 bl en_bar vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mupper_pmos2 br en_bar vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_precharge_0 + +.SUBCKT sram_2_16_1_scn4m_subm_precharge_array ++ bl_0 br_0 bl_1 br_1 bl_2 br_2 en_bar vdd +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* OUTPUT: bl_2 +* OUTPUT: br_2 +* INPUT : en_bar +* POWER : vdd +* cols: 3 size: 1 bl: bl br: br +Xpre_column_0 ++ bl_0 br_0 en_bar vdd ++ sram_2_16_1_scn4m_subm_precharge_0 +Xpre_column_1 ++ bl_1 br_1 en_bar vdd ++ sram_2_16_1_scn4m_subm_precharge_0 +Xpre_column_2 ++ bl_2 br_2 en_bar vdd ++ sram_2_16_1_scn4m_subm_precharge_0 +.ENDS sram_2_16_1_scn4m_subm_precharge_array +*********************** Write_Driver ****************************** +.SUBCKT write_driver din bl br en vdd gnd + +**** Inverter to conver Data_in to data_in_bar ****** +* din_bar = inv(din) +M_1 din_bar din gnd gnd n W=0.8u L=0.4u +M_2 din_bar din vdd vdd p W=1.4u L=0.4u + +**** 2input nand gate follwed by inverter to drive BL ****** +* din_bar_gated = nand(en, din) +M_3 din_bar_gated en net_7 gnd n W=1.4u L=0.4u +M_4 net_7 din gnd gnd n W=1.4u L=0.4u +M_5 din_bar_gated en vdd vdd p W=1.4u L=0.4u +M_6 din_bar_gated din vdd vdd p W=1.4u L=0.4u +* din_bar_gated_bar = inv(din_bar_gated) +M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=1.4u L=0.4u +M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=0.8u L=0.4u + +**** 2input nand gate follwed by inverter to drive BR****** +* din_gated = nand(en, din_bar) +M_9 din_gated en vdd vdd p W=1.4u L=0.4u +M_10 din_gated en net_8 gnd n W=1.4u L=0.4u +M_11 net_8 din_bar gnd gnd n W=1.4u L=0.4u +M_12 din_gated din_bar vdd vdd p W=1.4u L=0.4u +* din_gated_bar = inv(din_gated) +M_13 din_gated_bar din_gated vdd vdd p W=1.4u L=0.4u +M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u + +************************************************ +* pull down with en enable +M_15 bl din_gated_bar gnd gnd n W=2.4u L=0.4u +M_16 br din_bar_gated_bar gnd gnd n W=2.4u L=0.4u + + + +.ENDS $ write_driver + +.SUBCKT sram_2_16_1_scn4m_subm_write_driver_array ++ data_0 data_1 bl_0 br_0 bl_1 br_1 en vdd gnd +* INPUT : data_0 +* INPUT : data_1 +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* word_size 2 +Xwrite_driver0 ++ data_0 bl_0 br_0 en vdd gnd ++ write_driver +Xwrite_driver1 ++ data_1 bl_1 br_1 en vdd gnd ++ write_driver +.ENDS sram_2_16_1_scn4m_subm_write_driver_array + +.SUBCKT sram_2_16_1_scn4m_subm_port_data ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 dout_0 dout_1 din_0 din_1 s_en ++ p_en_bar w_en vdd gnd +* INOUT : rbl_bl +* INOUT : rbl_br +* INOUT : bl_0 +* INOUT : br_0 +* INOUT : bl_1 +* INOUT : br_1 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* INPUT : din_0 +* INPUT : din_1 +* INPUT : s_en +* INPUT : p_en_bar +* INPUT : w_en +* POWER : vdd +* GROUND: gnd +Xprecharge_array0 ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 p_en_bar vdd ++ sram_2_16_1_scn4m_subm_precharge_array +Xsense_amp_array0 ++ dout_0 bl_0 br_0 dout_1 bl_1 br_1 s_en vdd gnd ++ sram_2_16_1_scn4m_subm_sense_amp_array +Xwrite_driver_array0 ++ din_0 din_1 bl_0 br_0 bl_1 br_1 w_en vdd gnd ++ sram_2_16_1_scn4m_subm_write_driver_array +.ENDS sram_2_16_1_scn4m_subm_port_data + +* spice ptx M{0} {1} p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p + +* spice ptx M{0} {1} n m=1 w=0.8u l=0.4u pd=2.40u ps=2.40u as=0.80p ad=0.80p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpinv_nmos Z A gnd gnd n m=1 w=0.8u l=0.4u pd=2.40u ps=2.40u as=0.80p ad=0.80p +.ENDS sram_2_16_1_scn4m_subm_pinv + +* spice ptx M{0} {1} n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p + +* spice ptx M{0} {1} n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p + +.SUBCKT sram_2_16_1_scn4m_subm_pnand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_pnand2 + +.SUBCKT sram_2_16_1_scn4m_subm_and2_dec ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_scn4m_subm_pnand2 +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv +.ENDS sram_2_16_1_scn4m_subm_and2_dec + +.SUBCKT sram_2_16_1_scn4m_subm_hierarchical_predecode2x4 ++ in_0 in_1 out_0 out_1 out_2 out_3 vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* OUTPUT: out_0 +* OUTPUT: out_1 +* OUTPUT: out_2 +* OUTPUT: out_3 +* POWER : vdd +* GROUND: gnd +Xpre_inv_0 ++ in_0 inbar_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv +Xpre_inv_1 ++ in_1 inbar_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv +XXpre2x4_and_0 ++ inbar_0 inbar_1 out_0 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XXpre2x4_and_1 ++ in_0 inbar_1 out_1 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XXpre2x4_and_2 ++ inbar_0 in_1 out_2 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XXpre2x4_and_3 ++ in_0 in_1 out_3 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +.ENDS sram_2_16_1_scn4m_subm_hierarchical_predecode2x4 + +.SUBCKT sram_2_16_1_scn4m_subm_hierarchical_decoder ++ addr_0 addr_1 addr_2 addr_3 decode_0 decode_1 decode_2 decode_3 ++ decode_4 decode_5 decode_6 decode_7 decode_8 decode_9 decode_10 ++ decode_11 decode_12 decode_13 decode_14 decode_15 vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* OUTPUT: decode_0 +* OUTPUT: decode_1 +* OUTPUT: decode_2 +* OUTPUT: decode_3 +* OUTPUT: decode_4 +* OUTPUT: decode_5 +* OUTPUT: decode_6 +* OUTPUT: decode_7 +* OUTPUT: decode_8 +* OUTPUT: decode_9 +* OUTPUT: decode_10 +* OUTPUT: decode_11 +* OUTPUT: decode_12 +* OUTPUT: decode_13 +* OUTPUT: decode_14 +* OUTPUT: decode_15 +* POWER : vdd +* GROUND: gnd +Xpre_0 ++ addr_0 addr_1 out_0 out_1 out_2 out_3 vdd gnd ++ sram_2_16_1_scn4m_subm_hierarchical_predecode2x4 +Xpre_1 ++ addr_2 addr_3 out_4 out_5 out_6 out_7 vdd gnd ++ sram_2_16_1_scn4m_subm_hierarchical_predecode2x4 +XDEC_AND_0 ++ out_0 out_4 decode_0 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_4 ++ out_0 out_5 decode_4 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_8 ++ out_0 out_6 decode_8 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_12 ++ out_0 out_7 decode_12 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_1 ++ out_1 out_4 decode_1 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_5 ++ out_1 out_5 decode_5 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_9 ++ out_1 out_6 decode_9 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_13 ++ out_1 out_7 decode_13 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_2 ++ out_2 out_4 decode_2 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_6 ++ out_2 out_5 decode_6 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_10 ++ out_2 out_6 decode_10 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_14 ++ out_2 out_7 decode_14 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_3 ++ out_3 out_4 decode_3 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_7 ++ out_3 out_5 decode_7 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_11 ++ out_3 out_6 decode_11 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +XDEC_AND_15 ++ out_3 out_7 decode_15 vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec +.ENDS sram_2_16_1_scn4m_subm_hierarchical_decoder + +.SUBCKT sram_2_16_1_scn4m_subm_wordline_driver ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xwld_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_scn4m_subm_pnand2 +Xwl_driver ++ zb_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv +.ENDS sram_2_16_1_scn4m_subm_wordline_driver + +.SUBCKT sram_2_16_1_scn4m_subm_wordline_driver_array ++ in_0 in_1 in_2 in_3 in_4 in_5 in_6 in_7 in_8 in_9 in_10 in_11 in_12 ++ in_13 in_14 in_15 wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 wl_7 wl_8 wl_9 ++ wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 en vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* INPUT : in_2 +* INPUT : in_3 +* INPUT : in_4 +* INPUT : in_5 +* INPUT : in_6 +* INPUT : in_7 +* INPUT : in_8 +* INPUT : in_9 +* INPUT : in_10 +* INPUT : in_11 +* INPUT : in_12 +* INPUT : in_13 +* INPUT : in_14 +* INPUT : in_15 +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* rows: 16 cols: 2 +Xwl_driver_and0 ++ in_0 en wl_0 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and1 ++ in_1 en wl_1 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and2 ++ in_2 en wl_2 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and3 ++ in_3 en wl_3 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and4 ++ in_4 en wl_4 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and5 ++ in_5 en wl_5 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and6 ++ in_6 en wl_6 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and7 ++ in_7 en wl_7 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and8 ++ in_8 en wl_8 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and9 ++ in_9 en wl_9 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and10 ++ in_10 en wl_10 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and11 ++ in_11 en wl_11 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and12 ++ in_12 en wl_12 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and13 ++ in_13 en wl_13 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and14 ++ in_14 en wl_14 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +Xwl_driver_and15 ++ in_15 en wl_15 vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver +.ENDS sram_2_16_1_scn4m_subm_wordline_driver_array + +.SUBCKT sram_2_16_1_scn4m_subm_and2_dec_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_scn4m_subm_pnand2 +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv +.ENDS sram_2_16_1_scn4m_subm_and2_dec_0 + +.SUBCKT sram_2_16_1_scn4m_subm_port_address ++ addr_0 addr_1 addr_2 addr_3 wl_en wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 ++ wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 rbl_wl vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* INPUT : wl_en +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* OUTPUT: rbl_wl +* POWER : vdd +* GROUND: gnd +Xrow_decoder ++ addr_0 addr_1 addr_2 addr_3 dec_out_0 dec_out_1 dec_out_2 dec_out_3 ++ dec_out_4 dec_out_5 dec_out_6 dec_out_7 dec_out_8 dec_out_9 dec_out_10 ++ dec_out_11 dec_out_12 dec_out_13 dec_out_14 dec_out_15 vdd gnd ++ sram_2_16_1_scn4m_subm_hierarchical_decoder +Xwordline_driver ++ dec_out_0 dec_out_1 dec_out_2 dec_out_3 dec_out_4 dec_out_5 dec_out_6 ++ dec_out_7 dec_out_8 dec_out_9 dec_out_10 dec_out_11 dec_out_12 ++ dec_out_13 dec_out_14 dec_out_15 wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 ++ wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_en vdd gnd ++ sram_2_16_1_scn4m_subm_wordline_driver_array +Xrbl_driver ++ wl_en vdd rbl_wl vdd gnd ++ sram_2_16_1_scn4m_subm_and2_dec_0 +.ENDS sram_2_16_1_scn4m_subm_port_address + +.SUBCKT sram_2_16_1_scn4m_subm_bank ++ dout0_0 dout0_1 rbl_bl_0_0 din0_0 din0_1 addr0_0 addr0_1 addr0_2 ++ addr0_3 s_en0 p_en_bar0 w_en0 wl_en0 vdd gnd +* OUTPUT: dout0_0 +* OUTPUT: dout0_1 +* OUTPUT: rbl_bl_0_0 +* INPUT : din0_0 +* INPUT : din0_1 +* INPUT : addr0_0 +* INPUT : addr0_1 +* INPUT : addr0_2 +* INPUT : addr0_3 +* INPUT : s_en0 +* INPUT : p_en_bar0 +* INPUT : w_en0 +* INPUT : wl_en0 +* POWER : vdd +* GROUND: gnd +Xbitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 rbl_wl0 wl_0_0 ++ wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 ++ wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 vdd gnd ++ sram_2_16_1_scn4m_subm_capped_replica_bitcell_array +Xport_data0 ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 dout0_0 dout0_1 ++ din0_0 din0_1 s_en0 p_en_bar0 w_en0 vdd gnd ++ sram_2_16_1_scn4m_subm_port_data +Xport_address0 ++ addr0_0 addr0_1 addr0_2 addr0_3 wl_en0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 ++ wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 ++ wl_0_13 wl_0_14 wl_0_15 rbl_wl0 vdd gnd ++ sram_2_16_1_scn4m_subm_port_address +.ENDS sram_2_16_1_scn4m_subm_bank +*********************** "dff" ****************************** +* Positive edge-triggered FF +.SUBCKT dff D Q clk vdd gnd + +* SPICE3 file created from dff.ext - technology: scmos + +M1000 vdd clk a_24_24# vdd p w=8u l=0.4u +M1001 a_84_296# D vdd vdd p w=4u l=0.4u +M1002 a_104_24# clk a_84_296# vdd p w=4u l=0.4u +M1003 a_140_296# a_24_24# a_104_24# vdd p w=4u l=0.4u +M1004 vdd a_152_16# a_140_296# vdd p w=4u l=0.4u +M1005 a_152_16# a_104_24# vdd vdd p w=4u l=0.4u +M1006 a_260_296# a_152_16# vdd vdd p w=4u l=0.4u +M1007 a_280_24# a_24_24# a_260_296# vdd p w=4u l=0.4u +M1008 a_320_336# clk a_280_24# vdd p w=2u l=0.4u +M1009 vdd Q a_320_336# vdd p w=2u l=0.4u +M1010 gnd clk a_24_24# gnd n w=4u l=0.4u +M1011 Q a_280_24# vdd vdd p w=8u l=0.4u +M1012 a_84_24# D gnd gnd n w=2u l=0.4u +M1013 a_104_24# a_24_24# a_84_24# gnd n w=2u l=0.4u +M1014 a_140_24# clk a_104_24# gnd n w=2u l=0.4u +M1015 gnd a_152_16# a_140_24# gnd n w=2u l=0.4u +M1016 a_152_16# a_104_24# gnd gnd n w=2u l=0.4u +M1017 a_260_24# a_152_16# gnd gnd n w=2u l=0.4u +M1018 a_280_24# clk a_260_24# gnd n w=2u l=0.4u +M1019 a_320_24# a_24_24# a_280_24# gnd n w=2u l=0.4u +M1020 gnd Q a_320_24# gnd n w=2u l=0.4u +M1021 Q a_280_24# gnd gnd n w=4u l=0.4u + +.ENDS + +.SUBCKT sram_2_16_1_scn4m_subm_data_dff ++ din_0 din_1 dout_0 dout_1 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 1 cols: 2 +Xdff_r0_c0 ++ din_0 dout_0 clk vdd gnd ++ dff +Xdff_r0_c1 ++ din_1 dout_1 clk vdd gnd ++ dff +.ENDS sram_2_16_1_scn4m_subm_data_dff + +.SUBCKT sram_2_16_1_scn4m_subm_row_addr_dff ++ din_0 din_1 din_2 din_3 dout_0 dout_1 dout_2 dout_3 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* INPUT : din_3 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* OUTPUT: dout_3 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 4 cols: 1 +Xdff_r0_c0 ++ din_0 dout_0 clk vdd gnd ++ dff +Xdff_r1_c0 ++ din_1 dout_1 clk vdd gnd ++ dff +Xdff_r2_c0 ++ din_2 dout_2 clk vdd gnd ++ dff +Xdff_r3_c0 ++ din_3 dout_3 clk vdd gnd ++ dff +.ENDS sram_2_16_1_scn4m_subm_row_addr_dff + +* spice ptx M{0} {1} n m=1 w=4.0u l=0.4u pd=8.80u ps=8.80u as=4.00p ad=4.00p + +* spice ptx M{0} {1} p m=1 w=8.0u l=0.4u pd=16.80u ps=16.80u as=8.00p ad=8.00p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_7 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=8.0u l=0.4u pd=16.80u ps=16.80u as=8.00p ad=8.00p +Mpinv_nmos Z A gnd gnd n m=1 w=4.0u l=0.4u pd=8.80u ps=8.80u as=4.00p ad=4.00p +.ENDS sram_2_16_1_scn4m_subm_pinv_7 + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_5 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpinv_nmos Z A gnd gnd n m=1 w=0.8u l=0.4u pd=2.40u ps=2.40u as=0.80p ad=0.80p +.ENDS sram_2_16_1_scn4m_subm_pinv_5 + +.SUBCKT sram_2_16_1_scn4m_subm_pdriver_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 5] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_5 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_7 +.ENDS sram_2_16_1_scn4m_subm_pdriver_1 + +* spice ptx M{0} {1} n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p + +.SUBCKT sram_2_16_1_scn4m_subm_pnand3_0 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand3_pmos1 vdd A Z vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand3_pmos2 Z B vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand3_pmos3 Z C vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand3_nmos1 Z C net1 gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand3_nmos2 net1 B net2 gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand3_nmos3 net2 A gnd gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_pnand3_0 + +* spice ptx M{0} {1} n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p + +* spice ptx M{0} {1} p m=1 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_6 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p +Mpinv_nmos Z A gnd gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_pinv_6 + +.SUBCKT sram_2_16_1_scn4m_subm_pdriver_3 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [2] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_6 +.ENDS sram_2_16_1_scn4m_subm_pdriver_3 + +.SUBCKT sram_2_16_1_scn4m_subm_pand3_0 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_scn4m_subm_pnand3_0 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pdriver_3 +.ENDS sram_2_16_1_scn4m_subm_pand3_0 + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_10 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpinv_nmos Z A gnd gnd n m=1 w=0.8u l=0.4u pd=2.40u ps=2.40u as=0.80p ad=0.80p +.ENDS sram_2_16_1_scn4m_subm_pinv_10 + +.SUBCKT sram_2_16_1_scn4m_subm_delay_chain ++ in out vdd gnd +* INPUT : in +* OUTPUT: out +* POWER : vdd +* GROUND: gnd +* fanouts: [4, 4, 4, 4, 4, 4, 4, 4, 4] +Xdinv0 ++ in dout_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_0_0 ++ dout_1 n_0_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_0_1 ++ dout_1 n_0_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_0_2 ++ dout_1 n_0_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_0_3 ++ dout_1 n_0_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv1 ++ dout_1 dout_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_1_0 ++ dout_2 n_1_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_1_1 ++ dout_2 n_1_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_1_2 ++ dout_2 n_1_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_1_3 ++ dout_2 n_1_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv2 ++ dout_2 dout_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_2_0 ++ dout_3 n_2_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_2_1 ++ dout_3 n_2_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_2_2 ++ dout_3 n_2_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_2_3 ++ dout_3 n_2_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv3 ++ dout_3 dout_4 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_3_0 ++ dout_4 n_3_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_3_1 ++ dout_4 n_3_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_3_2 ++ dout_4 n_3_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_3_3 ++ dout_4 n_3_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv4 ++ dout_4 dout_5 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_4_0 ++ dout_5 n_4_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_4_1 ++ dout_5 n_4_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_4_2 ++ dout_5 n_4_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_4_3 ++ dout_5 n_4_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv5 ++ dout_5 dout_6 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_5_0 ++ dout_6 n_5_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_5_1 ++ dout_6 n_5_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_5_2 ++ dout_6 n_5_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_5_3 ++ dout_6 n_5_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv6 ++ dout_6 dout_7 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_6_0 ++ dout_7 n_6_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_6_1 ++ dout_7 n_6_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_6_2 ++ dout_7 n_6_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_6_3 ++ dout_7 n_6_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv7 ++ dout_7 dout_8 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_7_0 ++ dout_8 n_7_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_7_1 ++ dout_8 n_7_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_7_2 ++ dout_8 n_7_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_7_3 ++ dout_8 n_7_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdinv8 ++ dout_8 out vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_8_0 ++ out n_8_0 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_8_1 ++ out n_8_1 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_8_2 ++ out n_8_2 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +Xdload_8_3 ++ out n_8_3 vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_10 +.ENDS sram_2_16_1_scn4m_subm_delay_chain + +.SUBCKT sram_2_16_1_scn4m_subm_pnand2_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_pnand2_0 + +* spice ptx M{0} {1} p m=3 w=6.4u l=0.4u pd=13.60u ps=13.60u as=6.40p ad=6.40p + +* spice ptx M{0} {1} n m=3 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=3 w=6.4u l=0.4u pd=13.60u ps=13.60u as=6.40p ad=6.40p +Mpinv_nmos Z A gnd gnd n m=3 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p +.ENDS sram_2_16_1_scn4m_subm_pinv_2 + +.SUBCKT sram_2_16_1_scn4m_subm_pdriver ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [12] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_2 +.ENDS sram_2_16_1_scn4m_subm_pdriver + +.SUBCKT sram_2_16_1_scn4m_subm_pand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xpand2_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_scn4m_subm_pnand2_0 +Xpand2_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pdriver +.ENDS sram_2_16_1_scn4m_subm_pand2 + +.SUBCKT sram_2_16_1_scn4m_subm_pdriver_4 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 1] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_5 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_5 +.ENDS sram_2_16_1_scn4m_subm_pdriver_4 + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_3 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpinv_nmos Z A gnd gnd n m=1 w=0.8u l=0.4u pd=2.40u ps=2.40u as=0.80p ad=0.80p +.ENDS sram_2_16_1_scn4m_subm_pinv_3 + +* spice ptx M{0} {1} n m=2 w=4.0u l=0.4u pd=8.80u ps=8.80u as=4.00p ad=4.00p + +* spice ptx M{0} {1} p m=2 w=8.0u l=0.4u pd=16.80u ps=16.80u as=8.00p ad=8.00p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_9 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=2 w=8.0u l=0.4u pd=16.80u ps=16.80u as=8.00p ad=8.00p +Mpinv_nmos Z A gnd gnd n m=2 w=4.0u l=0.4u pd=8.80u ps=8.80u as=4.00p ad=4.00p +.ENDS sram_2_16_1_scn4m_subm_pinv_9 + +.SUBCKT sram_2_16_1_scn4m_subm_pdriver_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [10] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_9 +.ENDS sram_2_16_1_scn4m_subm_pdriver_2 + +.SUBCKT sram_2_16_1_scn4m_subm_pand3 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_scn4m_subm_pnand3_0 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pdriver_2 +.ENDS sram_2_16_1_scn4m_subm_pand3 + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p +Mpinv_nmos Z A gnd gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_pinv_0 + +* spice ptx M{0} {1} n m=1 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p + +* spice ptx M{0} {1} p m=1 w=6.4u l=0.4u pd=13.60u ps=13.60u as=6.40p ad=6.40p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=1 w=6.4u l=0.4u pd=13.60u ps=13.60u as=6.40p ad=6.40p +Mpinv_nmos Z A gnd gnd n m=1 w=3.2u l=0.4u pd=7.20u ps=7.20u as=3.20p ad=3.20p +.ENDS sram_2_16_1_scn4m_subm_pinv_1 + +.SUBCKT sram_2_16_1_scn4m_subm_dff_buf_0 ++ D Q Qb clk vdd gnd +* INPUT : D +* OUTPUT: Q +* OUTPUT: Qb +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* inv1: 2 inv2: 4 +Xdff_buf_dff ++ D qint clk vdd gnd ++ dff +Xdff_buf_inv1 ++ qint Qb vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_0 +Xdff_buf_inv2 ++ Qb Q vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_1 +.ENDS sram_2_16_1_scn4m_subm_dff_buf_0 + +.SUBCKT sram_2_16_1_scn4m_subm_dff_buf_array ++ din_0 din_1 dout_0 dout_bar_0 dout_1 dout_bar_1 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* OUTPUT: dout_0 +* OUTPUT: dout_bar_0 +* OUTPUT: dout_1 +* OUTPUT: dout_bar_1 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* inv1: 2 inv2: 4 +Xdff_r0_c0 ++ din_0 dout_0 dout_bar_0 clk vdd gnd ++ sram_2_16_1_scn4m_subm_dff_buf_0 +Xdff_r1_c0 ++ din_1 dout_1 dout_bar_1 clk vdd gnd ++ sram_2_16_1_scn4m_subm_dff_buf_0 +.ENDS sram_2_16_1_scn4m_subm_dff_buf_array + +* spice ptx M{0} {1} n m=3 w=4.0u l=0.4u pd=8.80u ps=8.80u as=4.00p ad=4.00p + +* spice ptx M{0} {1} p m=3 w=8.0u l=0.4u pd=16.80u ps=16.80u as=8.00p ad=8.00p + +.SUBCKT sram_2_16_1_scn4m_subm_pinv_8 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpinv_pmos Z A vdd vdd p m=3 w=8.0u l=0.4u pd=16.80u ps=16.80u as=8.00p ad=8.00p +Mpinv_nmos Z A gnd gnd n m=3 w=4.0u l=0.4u pd=8.80u ps=8.80u as=4.00p ad=4.00p +.ENDS sram_2_16_1_scn4m_subm_pinv_8 + +.SUBCKT sram_2_16_1_scn4m_subm_pdriver_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 2, 5, 15] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_5 +Xbuf_inv2 ++ Zb1_int Zb2_int vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_6 +Xbuf_inv3 ++ Zb2_int Zb3_int vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_7 +Xbuf_inv4 ++ Zb3_int Z vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_8 +.ENDS sram_2_16_1_scn4m_subm_pdriver_0 + +.SUBCKT sram_2_16_1_scn4m_subm_pnand2_1 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +Mpnand2_pmos1 vdd A Z vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_pmos2 Z B vdd vdd p m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_nmos1 Z B net1 gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +Mpnand2_nmos2 net1 A gnd gnd n m=1 w=1.6u l=0.4u pd=4.00u ps=4.00u as=1.60p ad=1.60p +.ENDS sram_2_16_1_scn4m_subm_pnand2_1 + +.SUBCKT sram_2_16_1_scn4m_subm_control_logic_rw ++ csb web clk rbl_bl s_en w_en p_en_bar wl_en clk_buf vdd gnd +* INPUT : csb +* INPUT : web +* INPUT : clk +* INPUT : rbl_bl +* OUTPUT: s_en +* OUTPUT: w_en +* OUTPUT: p_en_bar +* OUTPUT: wl_en +* OUTPUT: clk_buf +* POWER : vdd +* GROUND: gnd +* word_size 2 +Xctrl_dffs ++ csb web cs_bar cs we_bar we clk_buf vdd gnd ++ sram_2_16_1_scn4m_subm_dff_buf_array +Xclkbuf ++ clk clk_buf vdd gnd ++ sram_2_16_1_scn4m_subm_pdriver_0 +Xinv_clk_bar ++ clk_buf clk_bar vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_3 +Xand2_gated_clk_bar ++ clk_bar cs gated_clk_bar vdd gnd ++ sram_2_16_1_scn4m_subm_pand2 +Xand2_gated_clk_buf ++ clk_buf cs gated_clk_buf vdd gnd ++ sram_2_16_1_scn4m_subm_pand2 +Xbuf_wl_en ++ gated_clk_bar wl_en vdd gnd ++ sram_2_16_1_scn4m_subm_pdriver_1 +Xrbl_bl_delay_inv ++ rbl_bl_delay rbl_bl_delay_bar vdd gnd ++ sram_2_16_1_scn4m_subm_pinv_3 +Xw_en_and ++ we rbl_bl_delay_bar gated_clk_bar w_en vdd gnd ++ sram_2_16_1_scn4m_subm_pand3 +Xbuf_s_en_and ++ rbl_bl_delay gated_clk_bar we_bar s_en vdd gnd ++ sram_2_16_1_scn4m_subm_pand3_0 +Xdelay_chain ++ rbl_bl rbl_bl_delay vdd gnd ++ sram_2_16_1_scn4m_subm_delay_chain +Xnand_p_en_bar ++ gated_clk_buf rbl_bl_delay p_en_bar_unbuf vdd gnd ++ sram_2_16_1_scn4m_subm_pnand2_1 +Xbuf_p_en_bar ++ p_en_bar_unbuf p_en_bar vdd gnd ++ sram_2_16_1_scn4m_subm_pdriver_4 +.ENDS sram_2_16_1_scn4m_subm_control_logic_rw + +.SUBCKT sram_2_16_1_scn4m_subm ++ din0[0] din0[1] addr0[0] addr0[1] addr0[2] addr0[3] csb0 web0 clk0 ++ dout0[0] dout0[1] vdd gnd +* INPUT : din0[0] +* INPUT : din0[1] +* INPUT : addr0[0] +* INPUT : addr0[1] +* INPUT : addr0[2] +* INPUT : addr0[3] +* INPUT : csb0 +* INPUT : web0 +* INPUT : clk0 +* OUTPUT: dout0[0] +* OUTPUT: dout0[1] +* POWER : vdd +* GROUND: gnd +Xbank0 ++ dout0[0] dout0[1] rbl_bl0 bank_din0_0 bank_din0_1 a0_0 a0_1 a0_2 a0_3 ++ s_en0 p_en_bar0 w_en0 wl_en0 vdd gnd ++ sram_2_16_1_scn4m_subm_bank +Xcontrol0 ++ csb0 web0 clk0 rbl_bl0 s_en0 w_en0 p_en_bar0 wl_en0 clk_buf0 vdd gnd ++ sram_2_16_1_scn4m_subm_control_logic_rw +Xrow_address0 ++ addr0[0] addr0[1] addr0[2] addr0[3] a0_0 a0_1 a0_2 a0_3 clk_buf0 vdd ++ gnd ++ sram_2_16_1_scn4m_subm_row_addr_dff +Xdata_dff0 ++ din0[0] din0[1] bank_din0_0 bank_din0_1 clk_buf0 vdd gnd ++ sram_2_16_1_scn4m_subm_data_dff +.ENDS sram_2_16_1_scn4m_subm diff --git a/compiler/tests/sp_files/sram_2_16_1_sky130.sp b/compiler/tests/sp_files/sram_2_16_1_sky130.sp new file mode 100644 index 00000000..d68dde96 --- /dev/null +++ b/compiler/tests/sp_files/sram_2_16_1_sky130.sp @@ -0,0 +1,2400 @@ +************************************************** +* OpenRAM generated memory. +* Words: 16 +* Data bits: 2 +* Banks: 1 +* Column mux: 1:1 +* Trimmed: False +* LVS: False +************************************************** +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +* SPICE3 file created from sky130_fd_bd_sram__openram_dff.ext - technology: EFS8A + +.subckt sky130_fd_bd_sram__openram_dff D Q CLK VDD GND +X1000 a_511_725# a_n8_115# VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1001 a_353_115# CLK a_11_624# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1002 a_353_725# a_203_89# a_11_624# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1003 a_11_624# a_203_89# a_161_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1004 a_11_624# CLK a_161_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1005 GND Q a_703_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1006 VDD Q a_703_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1007 a_203_89# CLK GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1008 a_203_89# CLK VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1009 a_161_115# D GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1010 a_161_725# D VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1011 GND a_11_624# a_n8_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1012 a_703_115# a_203_89# ON GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1013 VDD a_11_624# a_n8_115# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1014 a_703_725# CLK ON VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1015 Q ON VDD VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1016 Q ON GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1017 ON a_203_89# a_511_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1018 ON CLK a_511_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1019 GND a_n8_115# a_353_115# GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X1020 VDD a_n8_115# a_353_725# VDD sky130_fd_pr__pfet_01v8 W=3 L=0.15 +X1021 a_511_115# a_n8_115# GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +.ends + +.SUBCKT sram_2_16_1_sky130_row_addr_dff ++ din_0 din_1 din_2 din_3 din_4 dout_0 dout_1 dout_2 dout_3 dout_4 clk ++ vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* INPUT : din_3 +* INPUT : din_4 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* OUTPUT: dout_3 +* OUTPUT: dout_4 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 5 cols: 1 +Xdff_r0_c0 ++ din_0 dout_0 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r1_c0 ++ din_1 dout_1 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r2_c0 ++ din_2 dout_2 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r3_c0 ++ din_3 dout_3 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r4_c0 ++ din_4 dout_4 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +.ENDS sram_2_16_1_sky130_row_addr_dff + +.SUBCKT sram_2_16_1_sky130_data_dff ++ din_0 din_1 din_2 dout_0 dout_1 dout_2 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 1 cols: 3 +Xdff_r0_c0 ++ din_0 dout_0 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r0_c1 ++ din_1 dout_1 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +Xdff_r0_c2 ++ din_2 dout_2 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +.ENDS sram_2_16_1_sky130_data_dff + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u + +.SUBCKT sram_2_16_1_sky130_pinv_dec ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_dec +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_nand2_dec.ext - technology: EFS8A + + +* Top level circuit sky130_fd_bd_sram__openram_sp_nand2_dec +.subckt sky130_fd_bd_sram__openram_sp_nand2_dec A B Z VDD GND + +X1001 Z B VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1002 VDD A Z VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1000 Z A a_n722_276# GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1003 a_n722_276# B GND GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +.ends + + +.SUBCKT sram_2_16_1_sky130_and2_dec ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand2_dec +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_and2_dec + +.SUBCKT sram_2_16_1_sky130_hierarchical_predecode2x4 ++ in_0 in_1 out_0 out_1 out_2 out_3 vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* OUTPUT: out_0 +* OUTPUT: out_1 +* OUTPUT: out_2 +* OUTPUT: out_3 +* POWER : vdd +* GROUND: gnd +Xpre_inv_0 ++ in_0 inbar_0 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +Xpre_inv_1 ++ in_1 inbar_1 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +XXpre2x4_and_0 ++ inbar_0 inbar_1 out_0 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XXpre2x4_and_1 ++ in_0 inbar_1 out_1 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XXpre2x4_and_2 ++ inbar_0 in_1 out_2 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XXpre2x4_and_3 ++ in_0 in_1 out_3 vdd gnd ++ sram_2_16_1_sky130_and2_dec +.ENDS sram_2_16_1_sky130_hierarchical_predecode2x4 +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_nand3_dec.ext - technology: EFS8A + + +* Top level circuit sky130_fd_bd_sram__openram_sp_nand3_dec +.subckt sky130_fd_bd_sram__openram_sp_nand3_dec A B C Z VDD GND + +X1001 Z A a_n346_328# GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1002 a_n346_256# C GND GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1003 a_n346_328# B a_n346_256# GND sky130_fd_pr__nfet_01v8 W=0.74 L=0.15 +X1000 Z B VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1004 Z A VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +X1005 Z C VDD VDD sky130_fd_pr__pfet_01v8 W=1.12 L=0.15 +.ends + + +.SUBCKT sram_2_16_1_sky130_and3_dec ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand3_dec_nand ++ A B C zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand3_dec +Xpand3_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_and3_dec + +.SUBCKT sram_2_16_1_sky130_hierarchical_predecode3x8 ++ in_0 in_1 in_2 out_0 out_1 out_2 out_3 out_4 out_5 out_6 out_7 vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* INPUT : in_2 +* OUTPUT: out_0 +* OUTPUT: out_1 +* OUTPUT: out_2 +* OUTPUT: out_3 +* OUTPUT: out_4 +* OUTPUT: out_5 +* OUTPUT: out_6 +* OUTPUT: out_7 +* POWER : vdd +* GROUND: gnd +Xpre_inv_0 ++ in_0 inbar_0 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +Xpre_inv_1 ++ in_1 inbar_1 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +Xpre_inv_2 ++ in_2 inbar_2 vdd gnd ++ sram_2_16_1_sky130_pinv_dec +XXpre3x8_and_0 ++ inbar_0 inbar_1 inbar_2 out_0 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_1 ++ in_0 inbar_1 inbar_2 out_1 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_2 ++ inbar_0 in_1 inbar_2 out_2 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_3 ++ in_0 in_1 inbar_2 out_3 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_4 ++ inbar_0 inbar_1 in_2 out_4 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_5 ++ in_0 inbar_1 in_2 out_5 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_6 ++ inbar_0 in_1 in_2 out_6 vdd gnd ++ sram_2_16_1_sky130_and3_dec +XXpre3x8_and_7 ++ in_0 in_1 in_2 out_7 vdd gnd ++ sram_2_16_1_sky130_and3_dec +.ENDS sram_2_16_1_sky130_hierarchical_predecode3x8 + +.SUBCKT sram_2_16_1_sky130_hierarchical_decoder ++ addr_0 addr_1 addr_2 addr_3 addr_4 decode_0 decode_1 decode_2 decode_3 ++ decode_4 decode_5 decode_6 decode_7 decode_8 decode_9 decode_10 ++ decode_11 decode_12 decode_13 decode_14 decode_15 decode_16 vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* INPUT : addr_4 +* OUTPUT: decode_0 +* OUTPUT: decode_1 +* OUTPUT: decode_2 +* OUTPUT: decode_3 +* OUTPUT: decode_4 +* OUTPUT: decode_5 +* OUTPUT: decode_6 +* OUTPUT: decode_7 +* OUTPUT: decode_8 +* OUTPUT: decode_9 +* OUTPUT: decode_10 +* OUTPUT: decode_11 +* OUTPUT: decode_12 +* OUTPUT: decode_13 +* OUTPUT: decode_14 +* OUTPUT: decode_15 +* OUTPUT: decode_16 +* POWER : vdd +* GROUND: gnd +Xpre_0 ++ addr_0 addr_1 out_0 out_1 out_2 out_3 vdd gnd ++ sram_2_16_1_sky130_hierarchical_predecode2x4 +Xpre3x8_0 ++ addr_2 addr_3 addr_4 out_4 out_5 out_6 out_7 out_8 out_9 out_10 out_11 ++ vdd gnd ++ sram_2_16_1_sky130_hierarchical_predecode3x8 +XDEC_AND_0 ++ out_0 out_4 decode_0 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_4 ++ out_0 out_5 decode_4 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_8 ++ out_0 out_6 decode_8 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_12 ++ out_0 out_7 decode_12 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_16 ++ out_0 out_8 decode_16 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_1 ++ out_1 out_4 decode_1 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_5 ++ out_1 out_5 decode_5 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_9 ++ out_1 out_6 decode_9 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_13 ++ out_1 out_7 decode_13 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_2 ++ out_2 out_4 decode_2 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_6 ++ out_2 out_5 decode_6 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_10 ++ out_2 out_6 decode_10 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_14 ++ out_2 out_7 decode_14 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_3 ++ out_3 out_4 decode_3 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_7 ++ out_3 out_5 decode_7 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_11 ++ out_3 out_6 decode_11 vdd gnd ++ sram_2_16_1_sky130_and2_dec +XDEC_AND_15 ++ out_3 out_7 decode_15 vdd gnd ++ sram_2_16_1_sky130_and2_dec +.ENDS sram_2_16_1_sky130_hierarchical_decoder + +.SUBCKT sram_2_16_1_sky130_and2_dec_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpand2_dec_nand ++ A B zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand2_dec +Xpand2_dec_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_and2_dec_0 + +.SUBCKT sram_2_16_1_sky130_wordline_driver ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* cols: 3 +Xwld_nand ++ A B zb_int vdd gnd ++ sky130_fd_bd_sram__openram_sp_nand2_dec +Xwl_driver ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_dec +.ENDS sram_2_16_1_sky130_wordline_driver + +.SUBCKT sram_2_16_1_sky130_wordline_driver_array ++ in_0 in_1 in_2 in_3 in_4 in_5 in_6 in_7 in_8 in_9 in_10 in_11 in_12 ++ in_13 in_14 in_15 in_16 wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 wl_6 wl_7 wl_8 ++ wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_16 en vdd gnd +* INPUT : in_0 +* INPUT : in_1 +* INPUT : in_2 +* INPUT : in_3 +* INPUT : in_4 +* INPUT : in_5 +* INPUT : in_6 +* INPUT : in_7 +* INPUT : in_8 +* INPUT : in_9 +* INPUT : in_10 +* INPUT : in_11 +* INPUT : in_12 +* INPUT : in_13 +* INPUT : in_14 +* INPUT : in_15 +* INPUT : in_16 +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* OUTPUT: wl_16 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +Xwl_driver_and0 ++ in_0 en wl_0 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and1 ++ in_1 en wl_1 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and2 ++ in_2 en wl_2 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and3 ++ in_3 en wl_3 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and4 ++ in_4 en wl_4 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and5 ++ in_5 en wl_5 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and6 ++ in_6 en wl_6 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and7 ++ in_7 en wl_7 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and8 ++ in_8 en wl_8 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and9 ++ in_9 en wl_9 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and10 ++ in_10 en wl_10 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and11 ++ in_11 en wl_11 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and12 ++ in_12 en wl_12 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and13 ++ in_13 en wl_13 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and14 ++ in_14 en wl_14 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and15 ++ in_15 en wl_15 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +Xwl_driver_and16 ++ in_16 en wl_16 vdd gnd ++ sram_2_16_1_sky130_wordline_driver +.ENDS sram_2_16_1_sky130_wordline_driver_array + +.SUBCKT sram_2_16_1_sky130_port_address ++ addr_0 addr_1 addr_2 addr_3 addr_4 wl_en wl_0 wl_1 wl_2 wl_3 wl_4 wl_5 ++ wl_6 wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_16 rbl_wl ++ vdd gnd +* INPUT : addr_0 +* INPUT : addr_1 +* INPUT : addr_2 +* INPUT : addr_3 +* INPUT : addr_4 +* INPUT : wl_en +* OUTPUT: wl_0 +* OUTPUT: wl_1 +* OUTPUT: wl_2 +* OUTPUT: wl_3 +* OUTPUT: wl_4 +* OUTPUT: wl_5 +* OUTPUT: wl_6 +* OUTPUT: wl_7 +* OUTPUT: wl_8 +* OUTPUT: wl_9 +* OUTPUT: wl_10 +* OUTPUT: wl_11 +* OUTPUT: wl_12 +* OUTPUT: wl_13 +* OUTPUT: wl_14 +* OUTPUT: wl_15 +* OUTPUT: wl_16 +* OUTPUT: rbl_wl +* POWER : vdd +* GROUND: gnd +Xrow_decoder ++ addr_0 addr_1 addr_2 addr_3 addr_4 dec_out_0 dec_out_1 dec_out_2 ++ dec_out_3 dec_out_4 dec_out_5 dec_out_6 dec_out_7 dec_out_8 dec_out_9 ++ dec_out_10 dec_out_11 dec_out_12 dec_out_13 dec_out_14 dec_out_15 ++ dec_out_16 vdd gnd ++ sram_2_16_1_sky130_hierarchical_decoder +Xwordline_driver ++ dec_out_0 dec_out_1 dec_out_2 dec_out_3 dec_out_4 dec_out_5 dec_out_6 ++ dec_out_7 dec_out_8 dec_out_9 dec_out_10 dec_out_11 dec_out_12 ++ dec_out_13 dec_out_14 dec_out_15 dec_out_16 wl_0 wl_1 wl_2 wl_3 wl_4 ++ wl_5 wl_6 wl_7 wl_8 wl_9 wl_10 wl_11 wl_12 wl_13 wl_14 wl_15 wl_16 ++ wl_en vdd gnd ++ sram_2_16_1_sky130_wordline_driver_array +Xrbl_driver ++ wl_en vdd rbl_wl vdd gnd ++ sram_2_16_1_sky130_and2_dec_0 +.ENDS sram_2_16_1_sky130_port_address +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colenda_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colenda_cent VPWR VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colenda.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colenda bl vdd gnd br gate vpb vnb +*X0 br gate br vnb sky130_fd_pr__special_nfet_pass w=0.065u l=0.17u +.ends + +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colenda_p_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colenda_p_cent VGND VPB VNB +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_col_cap_array_0 ++ fake_bl_0 fake_br_0 fake_bl_1 fake_br_1 fake_bl_2 fake_br_2 vdd gnd ++ gate +* OUTPUT: fake_bl_0 +* OUTPUT: fake_br_0 +* OUTPUT: fake_bl_1 +* OUTPUT: fake_br_1 +* OUTPUT: fake_bl_2 +* OUTPUT: fake_br_2 +* POWER : vdd +* GROUND: gnd +* BIAS : gate +Xrca_bottom_0 ++ fake_bl_0 vdd gnd fake_br_0 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +Xrca_bottom_1 ++ vdd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda_cent +Xrca_bottom_2 ++ fake_bl_1 vdd gnd fake_br_1 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +Xrca_bottom_3 ++ gnd vdd vnb ++ sky130_fd_bd_sram__sram_sp_colenda_p_cent +Xrca_bottom_4 ++ fake_bl_2 vdd gnd fake_br_2 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +.ENDS sram_2_16_1_sky130_sky130_col_cap_array_0 +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrap_p.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrap_p VGND +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_cell_opt1a_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_cell_opt1a_replica BL BR VGND VPWR VPB VNB WL +X0 VPWR WL BR VNB sky130_fd_pr__special_nfet_pass ad=4.375e+10p pd=920000u as=1.68e+10p ps=520000u w=140000u l=150000u +X1 Q VPWR VGND VNB sky130_fd_pr__special_nfet_latch ad=1.56e+11p pd=2.38e+06u as=8.08e+10p ps=1.28e+06u w=210000u l=150000u +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass ad=1.68e+10p pd=520000u as=4.25e+10p ps=920000u w=140000u l=150000u +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass ad=3.5e+10p pd=780000u as=0p ps=0u w=70000u l=95000u +*X4 VPWR WL VPWR VPB sky130_fd_pr__special_pfet_pass ad=9.72e+10p pd=1.86e+06u as=0p ps=0u w=70000u l=95000u +X5 VPWR Q VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X6 Q VPWR VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X7 VGND Q VPWR VNB sky130_fd_pr__special_nfet_latch ad=0p pd=0u as=0p ps=0u w=210000u l=150000u +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colend_p_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colend_p_cent VGND VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrapa_p.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrapa_p VGND +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_cell_opt1_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_cell_opt1_replica BL BR VGND VPWR VPB VNB WL +X0 VPWR WL BR VNB sky130_fd_pr__special_nfet_pass ad=4.375e+10p pd=920000u as=1.68e+10p ps=520000u w=140000u l=150000u +X1 Q VPWR VGND VNB sky130_fd_pr__special_nfet_latch ad=1.56e+11p pd=2.38e+06u as=8.08e+10p ps=1.28e+06u w=210000u l=150000u +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass ad=1.68e+10p pd=520000u as=4.25e+10p ps=920000u w=140000u l=150000u +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass ad=3.5e+10p pd=780000u as=0p ps=0u w=70000u l=95000u +*X4 VPWR WL VPWR VPB sky130_fd_pr__special_pfet_pass ad=9.72e+10p pd=1.86e+06u as=0p ps=0u w=70000u l=95000u +X5 VPWR Q VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X6 Q VPWR VPWR VPB sky130_fd_pr__special_pfet_pass ad=0p pd=0u as=0p ps=0u w=140000u l=150000u +X7 VGND Q VPWR VNB sky130_fd_pr__special_nfet_latch ad=0p pd=0u as=0p ps=0u w=210000u l=150000u +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colend.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colend bl vdd gnd br gate vpb vnb +*X0 br gate br vnb sky130_fd_pr__special_nfet_pass w=0.07u l=0.21u +.ends + + +.SUBCKT sram_2_16_1_sky130_sky130_replica_column ++ bl_0_0 br_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 ++ wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 wl_0_17 ++ wl_0_18 vdd gnd top_gate bot_gate +* OUTPUT: bl_0_0 +* OUTPUT: br_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* INPUT : wl_0_17 +* INPUT : wl_0_18 +* POWER : vdd +* GROUND: gnd +* INPUT : top_gate +* INPUT : bot_gate +Xrbc_0 ++ bl_0_0 vdd gnd br_0_0 top_gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +Xrbc_0_cap ++ gnd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend_p_cent +Xrbc_1 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_1_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_2 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_2_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_3 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_3_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_4 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_4_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_5 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_5_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_6 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_6_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_7 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_7_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_8 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_8_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_9 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_9_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_10 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_10_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_11 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_11_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_12 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_12_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_13 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_13_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_14 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_14_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_15 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_15_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_16 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_16_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_17 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_17 ++ sky130_fd_bd_sram__openram_sp_cell_opt1_replica +Xrbc_17_strap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrbc_18 ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_18 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_replica +Xrbc_18_strap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrbc_19 ++ bl_0_0 vdd gnd br_0_0 bot_gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda +Xrbc_19_cap ++ gnd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colenda_p_cent +.ENDS sram_2_16_1_sky130_sky130_replica_column +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_colend_cent.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_colend_cent VPWR VPB VNB +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_col_cap_array ++ fake_bl_0 fake_br_0 fake_bl_1 fake_br_1 fake_bl_2 fake_br_2 vdd gnd ++ gate +* OUTPUT: fake_bl_0 +* OUTPUT: fake_br_0 +* OUTPUT: fake_bl_1 +* OUTPUT: fake_br_1 +* OUTPUT: fake_bl_2 +* OUTPUT: fake_br_2 +* POWER : vdd +* GROUND: gnd +* BIAS : gate +Xrca_top_0 ++ fake_bl_0 vdd gnd fake_br_0 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +Xrca_top_1 ++ vdd vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend_cent +Xrca_top_2 ++ fake_bl_1 vdd gnd fake_br_1 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +Xrca_top_3 ++ gnd vdd vnb ++ sky130_fd_bd_sram__sram_sp_colend_p_cent +Xrca_top_4 ++ fake_bl_2 vdd gnd fake_br_2 gate vdd gnd ++ sky130_fd_bd_sram__sram_sp_colend +.ENDS sram_2_16_1_sky130_sky130_col_cap_array +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrapa.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrapa VPWR +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy BL BR VGND VPWR VPB VNB WL +X0 ll WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +X1 ul Q_bar_float VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +X2 BL WL ul VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +X5 VPWR Q_float lr VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X6 ur Q_bar_float VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X7 VGND Q_float ll VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_dummy_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* POWER : vdd +* GROUND: gnd +Xrow_0_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy +Xrow_0_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_0_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy +Xrow_0_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_0_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__openram_sp_cell_opt1a_dummy +.ENDS sram_2_16_1_sky130_sky130_dummy_array +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cornera.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cornera VNB VPWR VPB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cornerb.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cornerb VPWR VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_rowenda.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_rowenda VPWR WL +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_rowend.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_rowend VPWR WL +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_row_cap_array_0 ++ wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 ++ wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 wl_0_17 ++ wl_0_18 wl_0_19 vdd gnd +* OUTPUT: wl_0_0 +* OUTPUT: wl_0_1 +* OUTPUT: wl_0_2 +* OUTPUT: wl_0_3 +* OUTPUT: wl_0_4 +* OUTPUT: wl_0_5 +* OUTPUT: wl_0_6 +* OUTPUT: wl_0_7 +* OUTPUT: wl_0_8 +* OUTPUT: wl_0_9 +* OUTPUT: wl_0_10 +* OUTPUT: wl_0_11 +* OUTPUT: wl_0_12 +* OUTPUT: wl_0_13 +* OUTPUT: wl_0_14 +* OUTPUT: wl_0_15 +* OUTPUT: wl_0_16 +* OUTPUT: wl_0_17 +* OUTPUT: wl_0_18 +* OUTPUT: wl_0_19 +* POWER : vdd +* GROUND: gnd +Xrca_0 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_cornera +Xrca_1 ++ wl_0_0 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_2 ++ wl_0_1 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_3 ++ wl_0_2 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_4 ++ wl_0_3 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_5 ++ wl_0_4 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_6 ++ wl_0_5 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_7 ++ wl_0_6 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_8 ++ wl_0_7 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_9 ++ wl_0_8 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_10 ++ wl_0_9 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_11 ++ wl_0_10 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_12 ++ wl_0_11 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_13 ++ wl_0_12 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_14 ++ wl_0_13 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_15 ++ wl_0_14 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_16 ++ wl_0_15 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_17 ++ wl_0_16 vdd ++ sky130_fd_bd_sram__sram_sp_rowenda +Xrca_18 ++ wl_0_17 vdd ++ sky130_fd_bd_sram__sram_sp_rowend +Xrca_19 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_cornerb +.ENDS sram_2_16_1_sky130_sky130_row_cap_array_0 +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cell_opt1a.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cell_opt1a BL BR VGND VPWR VPB VNB WL +X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_cell_opt1.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_cell_opt1 BL BR VGND VPWR VPB VNB WL +X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15 +*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095 +X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15 +X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15 +.ends +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_wlstrap.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_wlstrap VPWR +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_bitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 wl_0_1 wl_0_2 wl_0_3 ++ wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 ++ wl_0_13 wl_0_14 wl_0_15 wl_0_16 vdd gnd +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +Xrow_0_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_0_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_0_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_0_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_0_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_0 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_1_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_1_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_1_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_1_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_1_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_1 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_2_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_2_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_2_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_2_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_2_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_2 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_3_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_3_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_3_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_3_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_3_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_3 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_4_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_4_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_4_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_4_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_4_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_4 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_5_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_5_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_5_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_5_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_5_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_5 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_6_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_6_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_6_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_6_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_6_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_6 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_7_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_7_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_7_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_7_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_7_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_7 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_8_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_8_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_8_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_8_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_8_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_8 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_9_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_9_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_9_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_9_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_9_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_9 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_10_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_10_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_10_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_10_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_10_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_10 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_11_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_11_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_11_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_11_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_11_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_11 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_12_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_12_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_12_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_12_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_12_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_12 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_13_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_13_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_13_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_13_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_13_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_13 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_14_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_14_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_14_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_14_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_14_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_14 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_15_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_15_col_0_wlstrapa ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrapa +Xrow_15_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_15_col_1_wlstrapa_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrapa_p +Xrow_15_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_15 ++ sky130_fd_bd_sram__sram_sp_cell_opt1a +Xrow_16_col_0_bitcell ++ bl_0_0 br_0_0 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_16_col_0_wlstrap ++ vdd ++ sky130_fd_bd_sram__sram_sp_wlstrap +Xrow_16_col_1_bitcell ++ bl_0_1 br_0_1 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +Xrow_16_col_1_wlstrap_p ++ gnd ++ sky130_fd_bd_sram__sram_sp_wlstrap_p +Xrow_16_col_2_bitcell ++ bl_0_2 br_0_2 gnd vdd vdd gnd wl_0_16 ++ sky130_fd_bd_sram__sram_sp_cell_opt1 +.ENDS sram_2_16_1_sky130_sky130_bitcell_array +* NGSPICE file created from sky130_fd_bd_sram__sram_sp_corner.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__sram_sp_corner VPWR VPB VNB +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_rowenda_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_rowenda_replica VPWR WL +.ends +* NGSPICE file created from sky130_fd_bd_sram__openram_sp_rowend_replica.ext - technology: sky130A + +.subckt sky130_fd_bd_sram__openram_sp_rowend_replica VPWR WL +.ends + +.SUBCKT sram_2_16_1_sky130_sky130_row_cap_array ++ wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 ++ wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 wl_0_17 ++ wl_0_18 wl_0_19 vdd gnd +* OUTPUT: wl_0_0 +* OUTPUT: wl_0_1 +* OUTPUT: wl_0_2 +* OUTPUT: wl_0_3 +* OUTPUT: wl_0_4 +* OUTPUT: wl_0_5 +* OUTPUT: wl_0_6 +* OUTPUT: wl_0_7 +* OUTPUT: wl_0_8 +* OUTPUT: wl_0_9 +* OUTPUT: wl_0_10 +* OUTPUT: wl_0_11 +* OUTPUT: wl_0_12 +* OUTPUT: wl_0_13 +* OUTPUT: wl_0_14 +* OUTPUT: wl_0_15 +* OUTPUT: wl_0_16 +* OUTPUT: wl_0_17 +* OUTPUT: wl_0_18 +* OUTPUT: wl_0_19 +* POWER : vdd +* GROUND: gnd +Xrca_0 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_cornera +Xrca_1 ++ wl_0_0 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_2 ++ wl_0_1 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_3 ++ wl_0_2 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_4 ++ wl_0_3 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_5 ++ wl_0_4 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_6 ++ wl_0_5 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_7 ++ wl_0_6 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_8 ++ wl_0_7 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_9 ++ wl_0_8 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_10 ++ wl_0_9 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_11 ++ wl_0_10 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_12 ++ wl_0_11 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_13 ++ wl_0_12 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_14 ++ wl_0_13 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_15 ++ wl_0_14 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_16 ++ wl_0_15 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_17 ++ wl_0_16 vdd ++ sky130_fd_bd_sram__openram_sp_rowenda_replica +Xrca_18 ++ wl_0_17 vdd ++ sky130_fd_bd_sram__openram_sp_rowend_replica +Xrca_19 ++ vdd gnd vdd ++ sky130_fd_bd_sram__sram_sp_corner +.ENDS sram_2_16_1_sky130_sky130_row_cap_array + +.SUBCKT sram_2_16_1_sky130_sky130_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xbitcell_array ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 wl_0_0 wl_0_1 wl_0_2 wl_0_3 ++ wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 ++ wl_0_13 wl_0_14 wl_0_15 wl_0_16 vdd gnd ++ sram_2_16_1_sky130_sky130_bitcell_array +Xreplica_col_0 ++ rbl_bl_0_0 rbl_br_0_0 rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 ++ wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 ++ wl_0_14 wl_0_15 wl_0_16 vdd gnd gnd gnd ++ sram_2_16_1_sky130_sky130_replica_column +Xdummy_row_0 ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 rbl_wl_0_0 vdd gnd ++ sram_2_16_1_sky130_sky130_dummy_array +Xdummy_row_bot ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 vdd gnd gnd ++ sram_2_16_1_sky130_sky130_col_cap_array_0 +Xdummy_row_top ++ bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 vdd gnd gnd ++ sram_2_16_1_sky130_sky130_col_cap_array +Xdummy_col_left ++ gnd rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ gnd vdd gnd ++ sram_2_16_1_sky130_sky130_row_cap_array +Xdummy_col_right ++ gnd rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ gnd vdd gnd ++ sram_2_16_1_sky130_sky130_row_cap_array_0 +.ENDS sram_2_16_1_sky130_sky130_replica_bitcell_array + +.SUBCKT sram_2_16_1_sky130_sky130_capped_replica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd +* INOUT : rbl_bl_0_0 +* INOUT : rbl_br_0_0 +* INOUT : bl_0_0 +* INOUT : br_0_0 +* INOUT : bl_0_1 +* INOUT : br_0_1 +* INOUT : bl_0_2 +* INOUT : br_0_2 +* INPUT : rbl_wl_0_0 +* INPUT : wl_0_0 +* INPUT : wl_0_1 +* INPUT : wl_0_2 +* INPUT : wl_0_3 +* INPUT : wl_0_4 +* INPUT : wl_0_5 +* INPUT : wl_0_6 +* INPUT : wl_0_7 +* INPUT : wl_0_8 +* INPUT : wl_0_9 +* INPUT : wl_0_10 +* INPUT : wl_0_11 +* INPUT : wl_0_12 +* INPUT : wl_0_13 +* INPUT : wl_0_14 +* INPUT : wl_0_15 +* INPUT : wl_0_16 +* POWER : vdd +* GROUND: gnd +* rows: 17 cols: 3 +* rbl: [1, 0] left_rbl: [0] right_rbl: [] +Xreplica_bitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl_0_0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 ++ wl_0_8 wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 ++ vdd gnd ++ sram_2_16_1_sky130_sky130_replica_bitcell_array +.ENDS sram_2_16_1_sky130_sky130_capped_replica_bitcell_array +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +*********************** "sky130_fd_bd_sram__openram_write_driver" ****************************** + +.SUBCKT sky130_fd_bd_sram__openram_write_driver DIN BL BR EN VDD GND + +**** Inverter to conver Data_in to data_in_bar ****** +* din_bar = inv(DIN) +X_1 din_bar DIN GND GND sky130_fd_pr__nfet_01v8 W=0.36 L=0.15 +X_2 din_bar DIN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 + +**** 2input nand gate follwed by inverter to drive BL ****** +* din_bar_gated = nand(EN, DIN) +X_3 din_bar_gated EN net_7 GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_4 net_7 DIN GND GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_5 din_bar_gated EN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_6 din_bar_gated DIN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +* din_bar_gated_bar = inv(din_bar_gated) +X_7 din_bar_gated_bar din_bar_gated VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_8 din_bar_gated_bar din_bar_gated GND GND sky130_fd_pr__nfet_01v8 W=0.36 L=0.15 + +**** 2input nand gate follwed by inverter to drive BR****** +* din_gated = nand(EN, din_bar) +X_9 din_gated EN VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_10 din_gated EN net_8 GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_11 net_8 din_bar GND GND sky130_fd_pr__nfet_01v8 W=0.55 L=0.15 +X_12 din_gated din_bar VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +* din_gated_bar = inv(din_gated) +X_13 din_gated_bar din_gated VDD VDD sky130_fd_pr__pfet_01v8 W=0.55 L=0.15 +X_14 din_gated_bar din_gated GND GND sky130_fd_pr__nfet_01v8 W=0.36 L=0.15 + +************************************************ +* pull down with EN enable +X_15 BL din_gated_bar GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 +X_16 BR din_bar_gated_bar GND GND sky130_fd_pr__nfet_01v8 W=1 L=0.15 + +.ENDS sky130_fd_bd_sram__openram_write_driver + +.SUBCKT sram_2_16_1_sky130_write_driver_array ++ data_0 data_1 data_2 bl_0 br_0 bl_1 br_1 bl_2 br_2 en_0 en_1 vdd gnd +* INPUT : data_0 +* INPUT : data_1 +* INPUT : data_2 +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* OUTPUT: bl_2 +* OUTPUT: br_2 +* INPUT : en_0 +* INPUT : en_1 +* POWER : vdd +* GROUND: gnd +* columns: 2 +* word_size 2 +Xwrite_driver0 ++ data_0 bl_0 br_0 en_0 vdd gnd ++ sky130_fd_bd_sram__openram_write_driver +Xwrite_driver1 ++ data_1 bl_1 br_1 en_0 vdd gnd ++ sky130_fd_bd_sram__openram_write_driver +Xwrite_driver2 ++ data_2 bl_2 br_2 en_1 vdd gnd ++ sky130_fd_bd_sram__openram_write_driver +.ENDS sram_2_16_1_sky130_write_driver_array + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u + +.SUBCKT sram_2_16_1_sky130_precharge_0 ++ bl br en_bar vdd +* OUTPUT: bl +* OUTPUT: br +* INPUT : en_bar +* POWER : vdd +Xlower_pmos bl en_bar br vdd sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u +Xupper_pmos1 bl en_bar vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u +Xupper_pmos2 br en_bar vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=0.55 l=0.15 pd=1.40 ps=1.40 as=0.21u ad=0.21u +.ENDS sram_2_16_1_sky130_precharge_0 + +.SUBCKT sram_2_16_1_sky130_precharge_array ++ bl_0 br_0 bl_1 br_1 bl_2 br_2 bl_3 br_3 en_bar vdd +* OUTPUT: bl_0 +* OUTPUT: br_0 +* OUTPUT: bl_1 +* OUTPUT: br_1 +* OUTPUT: bl_2 +* OUTPUT: br_2 +* OUTPUT: bl_3 +* OUTPUT: br_3 +* INPUT : en_bar +* POWER : vdd +* cols: 4 size: 1 bl: bl br: br +Xpre_column_0 ++ bl_0 br_0 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +Xpre_column_1 ++ bl_1 br_1 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +Xpre_column_2 ++ bl_2 br_2 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +Xpre_column_3 ++ bl_3 br_3 en_bar vdd ++ sram_2_16_1_sky130_precharge_0 +.ENDS sram_2_16_1_sky130_precharge_array +* Copyright 2020 The SkyWater PDK Authors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +* +* SPDX-License-Identifier: Apache-2.0 + +*********************** "sky130_fd_bd_sram__openram_sense_amp" ****************************** + +.SUBCKT sky130_fd_bd_sram__openram_sense_amp BL BR DOUT EN VDD GND +X1000 GND EN a_56_432# GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 +X1001 a_56_432# dint_bar dint GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 +X1002 dint_bar dint a_56_432# GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 + +X1003 VDD dint_bar dint VDD sky130_fd_pr__pfet_01v8 W=1.26 L=0.15 +X1004 dint_bar dint VDD VDD sky130_fd_pr__pfet_01v8 W=1.26 L=0.15 + +X1005 BL EN dint VDD sky130_fd_pr__pfet_01v8 W=2 L=0.15 +X1006 dint_bar EN BR VDD sky130_fd_pr__pfet_01v8 W=2 L=0.15 + +X1007 VDD dint_bar DOUT VDD sky130_fd_pr__pfet_01v8 W=1.26 L=0.15 +X1008 DOUT dint_bar GND GND sky130_fd_pr__nfet_01v8 W=0.65 L=0.15 + +.ENDS sky130_fd_bd_sram__openram_sense_amp + +.SUBCKT sram_2_16_1_sky130_sense_amp_array ++ data_0 bl_0 br_0 data_1 bl_1 br_1 data_2 bl_2 br_2 en vdd gnd +* OUTPUT: data_0 +* INPUT : bl_0 +* INPUT : br_0 +* OUTPUT: data_1 +* INPUT : bl_1 +* INPUT : br_1 +* OUTPUT: data_2 +* INPUT : bl_2 +* INPUT : br_2 +* INPUT : en +* POWER : vdd +* GROUND: gnd +* word_size 2 +* words_per_row: 1 +Xsa_d0 ++ bl_0 br_0 data_0 en vdd gnd ++ sky130_fd_bd_sram__openram_sense_amp +Xsa_d1 ++ bl_1 br_1 data_1 en vdd gnd ++ sky130_fd_bd_sram__openram_sense_amp +Xsa_d2 ++ bl_2 br_2 data_2 en vdd gnd ++ sky130_fd_bd_sram__openram_sense_amp +.ENDS sram_2_16_1_sky130_sense_amp_array + +.SUBCKT sram_2_16_1_sky130_port_data ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 sparebl_0 sparebr_0 dout_0 dout_1 ++ dout_2 din_0 din_1 din_2 s_en p_en_bar w_en bank_spare_wen0 vdd gnd +* INOUT : rbl_bl +* INOUT : rbl_br +* INOUT : bl_0 +* INOUT : br_0 +* INOUT : bl_1 +* INOUT : br_1 +* INOUT : sparebl_0 +* INOUT : sparebr_0 +* OUTPUT: dout_0 +* OUTPUT: dout_1 +* OUTPUT: dout_2 +* INPUT : din_0 +* INPUT : din_1 +* INPUT : din_2 +* INPUT : s_en +* INPUT : p_en_bar +* INPUT : w_en +* INPUT : bank_spare_wen0 +* POWER : vdd +* GROUND: gnd +Xprecharge_array0 ++ rbl_bl rbl_br bl_0 br_0 bl_1 br_1 sparebl_0 sparebr_0 p_en_bar vdd ++ sram_2_16_1_sky130_precharge_array +Xsense_amp_array0 ++ dout_0 bl_0 br_0 dout_1 bl_1 br_1 dout_2 sparebl_0 sparebr_0 s_en vdd ++ gnd ++ sram_2_16_1_sky130_sense_amp_array +Xwrite_driver_array0 ++ din_0 din_1 din_2 bl_0 br_0 bl_1 br_1 sparebl_0 sparebr_0 w_en ++ bank_spare_wen0 vdd gnd ++ sram_2_16_1_sky130_write_driver_array +.ENDS sram_2_16_1_sky130_port_data + +.SUBCKT sram_2_16_1_sky130_bank ++ dout0_0 dout0_1 dout0_2 rbl_bl_0_0 din0_0 din0_1 din0_2 addr0_0 ++ addr0_1 addr0_2 addr0_3 addr0_4 s_en0 p_en_bar0 w_en0 ++ bank_spare_wen0_0 wl_en0 vdd gnd +* OUTPUT: dout0_0 +* OUTPUT: dout0_1 +* OUTPUT: dout0_2 +* OUTPUT: rbl_bl_0_0 +* INPUT : din0_0 +* INPUT : din0_1 +* INPUT : din0_2 +* INPUT : addr0_0 +* INPUT : addr0_1 +* INPUT : addr0_2 +* INPUT : addr0_3 +* INPUT : addr0_4 +* INPUT : s_en0 +* INPUT : p_en_bar0 +* INPUT : w_en0 +* INPUT : bank_spare_wen0_0 +* INPUT : wl_en0 +* POWER : vdd +* GROUND: gnd +Xbitcell_array ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ rbl_wl0 wl_0_0 wl_0_1 wl_0_2 wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 ++ wl_0_9 wl_0_10 wl_0_11 wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 vdd gnd ++ sram_2_16_1_sky130_sky130_capped_replica_bitcell_array +Xport_data0 ++ rbl_bl_0_0 rbl_br_0_0 bl_0_0 br_0_0 bl_0_1 br_0_1 bl_0_2 br_0_2 ++ dout0_0 dout0_1 dout0_2 din0_0 din0_1 din0_2 s_en0 p_en_bar0 w_en0 ++ bank_spare_wen0_0 vdd gnd ++ sram_2_16_1_sky130_port_data +Xport_address0 ++ addr0_0 addr0_1 addr0_2 addr0_3 addr0_4 wl_en0 wl_0_0 wl_0_1 wl_0_2 ++ wl_0_3 wl_0_4 wl_0_5 wl_0_6 wl_0_7 wl_0_8 wl_0_9 wl_0_10 wl_0_11 ++ wl_0_12 wl_0_13 wl_0_14 wl_0_15 wl_0_16 rbl_wl0 vdd gnd ++ sram_2_16_1_sky130_port_address +.ENDS sram_2_16_1_sky130_bank + +.SUBCKT sram_2_16_1_sky130_pinv_4 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_4 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +.SUBCKT sram_2_16_1_sky130_pinv_8 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 5 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=3 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +.ENDS sram_2_16_1_sky130_pinv_8 + +.SUBCKT sram_2_16_1_sky130_pdriver_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 5] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_sky130_pinv_4 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_8 +.ENDS sram_2_16_1_sky130_pdriver_1 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +.SUBCKT sram_2_16_1_sky130_pinv_9 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 10 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=6 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +.ENDS sram_2_16_1_sky130_pinv_9 + +.SUBCKT sram_2_16_1_sky130_pdriver_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [10] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_sky130_pinv_9 +.ENDS sram_2_16_1_sky130_pdriver_2 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +.SUBCKT sram_2_16_1_sky130_pnand3 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpnand3_pmos1 vdd A Z vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand3_pmos2 Z B vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand3_pmos3 Z C vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand3_nmos1 Z C net1 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand3_nmos2 net1 B net2 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand3_nmos3 net2 A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pnand3 + +.SUBCKT sram_2_16_1_sky130_pand3 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 10 +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_sky130_pnand3 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pdriver_2 +.ENDS sram_2_16_1_sky130_pand3 + +.SUBCKT sram_2_16_1_sky130_pdriver_4 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 1] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_sky130_pinv_4 +Xbuf_inv2 ++ Zb1_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_4 +.ENDS sram_2_16_1_sky130_pdriver_4 + +.SUBCKT sram_2_16_1_sky130_pnand2_0 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpnand2_pmos1 vdd A Z vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_pmos2 Z B vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_nmos1 Z B net1 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand2_nmos2 net1 A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pnand2_0 + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=7 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=7 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +.SUBCKT sram_2_16_1_sky130_pinv_1 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 12 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=7 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=7 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +.ENDS sram_2_16_1_sky130_pinv_1 + +.SUBCKT sram_2_16_1_sky130_pdriver ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [12] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_sky130_pinv_1 +.ENDS sram_2_16_1_sky130_pdriver + +.SUBCKT sram_2_16_1_sky130_pnand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpnand2_pmos1 vdd A Z vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_pmos2 Z B vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpnand2_nmos1 Z B net1 gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +Xpnand2_nmos2 net1 A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pnand2 + +.SUBCKT sram_2_16_1_sky130_pand2 ++ A B Z vdd gnd +* INPUT : A +* INPUT : B +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 12 +Xpand2_nand ++ A B zb_int vdd gnd ++ sram_2_16_1_sky130_pnand2 +Xpand2_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pdriver +.ENDS sram_2_16_1_sky130_pand2 + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=10 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=10 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +.SUBCKT sram_2_16_1_sky130_pinv_7 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 17 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=10 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=10 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +.ENDS sram_2_16_1_sky130_pinv_7 + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=4 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=4 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u + +.SUBCKT sram_2_16_1_sky130_pinv_6 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 6 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=4 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=4 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +.ENDS sram_2_16_1_sky130_pinv_6 + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=2 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u + +.SUBCKT sram_2_16_1_sky130_pinv_5 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 2 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=2 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pinv_5 + +.SUBCKT sram_2_16_1_sky130_pdriver_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [1, 2, 6, 17] +Xbuf_inv1 ++ A Zb1_int vdd gnd ++ sram_2_16_1_sky130_pinv_4 +Xbuf_inv2 ++ Zb1_int Zb2_int vdd gnd ++ sram_2_16_1_sky130_pinv_5 +Xbuf_inv3 ++ Zb2_int Zb3_int vdd gnd ++ sram_2_16_1_sky130_pinv_6 +Xbuf_inv4 ++ Zb3_int Z vdd gnd ++ sram_2_16_1_sky130_pinv_7 +.ENDS sram_2_16_1_sky130_pdriver_0 + +.SUBCKT sram_2_16_1_sky130_pinv_11 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_11 + +.SUBCKT sram_2_16_1_sky130_delay_chain ++ in out vdd gnd +* INPUT : in +* OUTPUT: out +* POWER : vdd +* GROUND: gnd +* fanouts: [4, 4, 4, 4, 4, 4, 4, 4, 4] +Xdinv0 ++ in dout_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_0 ++ dout_1 n_0_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_1 ++ dout_1 n_0_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_2 ++ dout_1 n_0_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_0_3 ++ dout_1 n_0_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv1 ++ dout_1 dout_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_0 ++ dout_2 n_1_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_1 ++ dout_2 n_1_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_2 ++ dout_2 n_1_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_1_3 ++ dout_2 n_1_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv2 ++ dout_2 dout_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_0 ++ dout_3 n_2_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_1 ++ dout_3 n_2_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_2 ++ dout_3 n_2_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_2_3 ++ dout_3 n_2_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv3 ++ dout_3 dout_4 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_0 ++ dout_4 n_3_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_1 ++ dout_4 n_3_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_2 ++ dout_4 n_3_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_3_3 ++ dout_4 n_3_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv4 ++ dout_4 dout_5 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_0 ++ dout_5 n_4_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_1 ++ dout_5 n_4_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_2 ++ dout_5 n_4_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_4_3 ++ dout_5 n_4_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv5 ++ dout_5 dout_6 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_0 ++ dout_6 n_5_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_1 ++ dout_6 n_5_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_2 ++ dout_6 n_5_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_5_3 ++ dout_6 n_5_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv6 ++ dout_6 dout_7 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_0 ++ dout_7 n_6_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_1 ++ dout_7 n_6_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_2 ++ dout_7 n_6_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_6_3 ++ dout_7 n_6_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv7 ++ dout_7 dout_8 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_0 ++ dout_8 n_7_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_1 ++ dout_8 n_7_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_2 ++ dout_8 n_7_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_7_3 ++ dout_8 n_7_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdinv8 ++ dout_8 out vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_0 ++ out n_8_0 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_1 ++ out n_8_1 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_2 ++ out n_8_2 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +Xdload_8_3 ++ out n_8_3 vdd gnd ++ sram_2_16_1_sky130_pinv_11 +.ENDS sram_2_16_1_sky130_delay_chain + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=2 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u + +.SUBCKT sram_2_16_1_sky130_pinv_10 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 3 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=2 w=2.0 l=0.15 pd=4.30 ps=4.30 as=0.75u ad=0.75u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +.ENDS sram_2_16_1_sky130_pinv_10 + +.SUBCKT sram_2_16_1_sky130_pdriver_3 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* sizes: [3] +Xbuf_inv1 ++ A Z vdd gnd ++ sram_2_16_1_sky130_pinv_10 +.ENDS sram_2_16_1_sky130_pdriver_3 + +.SUBCKT sram_2_16_1_sky130_pand3_0 ++ A B C Z vdd gnd +* INPUT : A +* INPUT : B +* INPUT : C +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 3 +Xpand3_nand ++ A B C zb_int vdd gnd ++ sram_2_16_1_sky130_pnand3 +Xpand3_inv ++ zb_int Z vdd gnd ++ sram_2_16_1_sky130_pdriver_3 +.ENDS sram_2_16_1_sky130_pand3_0 + +.SUBCKT sram_2_16_1_sky130_pinv ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 2 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=2 w=1.26 l=0.15 pd=2.82 ps=2.82 as=0.47u ad=0.47u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=2 w=0.74 l=0.15 pd=1.78 ps=1.78 as=0.28u ad=0.28u +.ENDS sram_2_16_1_sky130_pinv + +* spice ptx X{0} {1} sky130_fd_pr__nfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +* spice ptx X{0} {1} sky130_fd_pr__pfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u + +.SUBCKT sram_2_16_1_sky130_pinv_0 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 4 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=3 w=1.68 l=0.15 pd=3.66 ps=3.66 as=0.63u ad=0.63u +.ENDS sram_2_16_1_sky130_pinv_0 + +.SUBCKT sram_2_16_1_sky130_dff_buf_0 ++ D Q Qb clk vdd gnd +* INPUT : D +* OUTPUT: Q +* OUTPUT: Qb +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* inv1: 2 inv2: 4 +Xdff_buf_dff ++ D qint clk vdd gnd ++ sky130_fd_bd_sram__openram_dff +Xdff_buf_inv1 ++ qint Qb vdd gnd ++ sram_2_16_1_sky130_pinv +Xdff_buf_inv2 ++ Qb Q vdd gnd ++ sram_2_16_1_sky130_pinv_0 +.ENDS sram_2_16_1_sky130_dff_buf_0 + +.SUBCKT sram_2_16_1_sky130_dff_buf_array ++ din_0 din_1 dout_0 dout_bar_0 dout_1 dout_bar_1 clk vdd gnd +* INPUT : din_0 +* INPUT : din_1 +* OUTPUT: dout_0 +* OUTPUT: dout_bar_0 +* OUTPUT: dout_1 +* OUTPUT: dout_bar_1 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 2 cols: 1 +* inv1: 2 inv2: 4 +Xdff_r0_c0 ++ din_0 dout_0 dout_bar_0 clk vdd gnd ++ sram_2_16_1_sky130_dff_buf_0 +Xdff_r1_c0 ++ din_1 dout_1 dout_bar_1 clk vdd gnd ++ sram_2_16_1_sky130_dff_buf_0 +.ENDS sram_2_16_1_sky130_dff_buf_array + +.SUBCKT sram_2_16_1_sky130_pinv_2 ++ A Z vdd gnd +* INPUT : A +* OUTPUT: Z +* POWER : vdd +* GROUND: gnd +* size: 1 +Xpinv_pmos Z A vdd vdd sky130_fd_pr__pfet_01v8 m=1 w=1.12 l=0.15 pd=2.54 ps=2.54 as=0.42u ad=0.42u +Xpinv_nmos Z A gnd gnd sky130_fd_pr__nfet_01v8 m=1 w=0.36 l=0.15 pd=1.02 ps=1.02 as=0.14u ad=0.14u +.ENDS sram_2_16_1_sky130_pinv_2 + +.SUBCKT sram_2_16_1_sky130_control_logic_rw ++ csb web clk rbl_bl s_en w_en p_en_bar wl_en clk_buf vdd gnd +* INPUT : csb +* INPUT : web +* INPUT : clk +* INPUT : rbl_bl +* OUTPUT: s_en +* OUTPUT: w_en +* OUTPUT: p_en_bar +* OUTPUT: wl_en +* OUTPUT: clk_buf +* POWER : vdd +* GROUND: gnd +* num_rows: 17 +* words_per_row: 1 +* word_size 2 +Xctrl_dffs ++ csb web cs_bar cs we_bar we clk_buf vdd gnd ++ sram_2_16_1_sky130_dff_buf_array +Xclkbuf ++ clk clk_buf vdd gnd ++ sram_2_16_1_sky130_pdriver_0 +Xinv_clk_bar ++ clk_buf clk_bar vdd gnd ++ sram_2_16_1_sky130_pinv_2 +Xand2_gated_clk_bar ++ clk_bar cs gated_clk_bar vdd gnd ++ sram_2_16_1_sky130_pand2 +Xand2_gated_clk_buf ++ clk_buf cs gated_clk_buf vdd gnd ++ sram_2_16_1_sky130_pand2 +Xbuf_wl_en ++ gated_clk_bar wl_en vdd gnd ++ sram_2_16_1_sky130_pdriver_1 +Xrbl_bl_delay_inv ++ rbl_bl_delay rbl_bl_delay_bar vdd gnd ++ sram_2_16_1_sky130_pinv_2 +Xw_en_and ++ we rbl_bl_delay_bar gated_clk_bar w_en vdd gnd ++ sram_2_16_1_sky130_pand3 +Xbuf_s_en_and ++ rbl_bl_delay gated_clk_bar we_bar s_en vdd gnd ++ sram_2_16_1_sky130_pand3_0 +Xdelay_chain ++ rbl_bl rbl_bl_delay vdd gnd ++ sram_2_16_1_sky130_delay_chain +Xnand_p_en_bar ++ gated_clk_buf rbl_bl_delay p_en_bar_unbuf vdd gnd ++ sram_2_16_1_sky130_pnand2_0 +Xbuf_p_en_bar ++ p_en_bar_unbuf p_en_bar vdd gnd ++ sram_2_16_1_sky130_pdriver_4 +.ENDS sram_2_16_1_sky130_control_logic_rw + +.SUBCKT sram_2_16_1_sky130_spare_wen_dff ++ din_0 dout_0 clk vdd gnd +* INPUT : din_0 +* OUTPUT: dout_0 +* INPUT : clk +* POWER : vdd +* GROUND: gnd +* rows: 1 cols: 1 +Xdff_r0_c0 ++ din_0 dout_0 CLK VDD GND ++ sky130_fd_bd_sram__openram_dff +.ENDS sram_2_16_1_sky130_spare_wen_dff + +.SUBCKT sram_2_16_1_sky130 ++ din0[0] din0[1] din0[2] addr0[0] addr0[1] addr0[2] addr0[3] addr0[4] ++ csb0 web0 clk0 spare_wen0 dout0[0] dout0[1] dout0[2] vccd1 vssd1 +* INPUT : din0[0] +* INPUT : din0[1] +* INPUT : din0[2] +* INPUT : addr0[0] +* INPUT : addr0[1] +* INPUT : addr0[2] +* INPUT : addr0[3] +* INPUT : addr0[4] +* INPUT : csb0 +* INPUT : web0 +* INPUT : clk0 +* INPUT : spare_wen0 +* OUTPUT: dout0[0] +* OUTPUT: dout0[1] +* OUTPUT: dout0[2] +* POWER : vccd1 +* GROUND: vssd1 +Xbank0 ++ dout0[0] dout0[1] dout0[2] rbl_bl0 bank_din0_0 bank_din0_1 bank_din0_2 ++ a0_0 a0_1 a0_2 a0_3 a0_4 s_en0 p_en_bar0 w_en0 bank_spare_wen0_0 ++ wl_en0 vccd1 vssd1 ++ sram_2_16_1_sky130_bank +Xcontrol0 ++ csb0 web0 clk0 rbl_bl0 s_en0 w_en0 p_en_bar0 wl_en0 clk_buf0 vccd1 ++ vssd1 ++ sram_2_16_1_sky130_control_logic_rw +Xrow_address0 ++ addr0[0] addr0[1] addr0[2] addr0[3] addr0[4] a0_0 a0_1 a0_2 a0_3 a0_4 ++ clk_buf0 vccd1 vssd1 ++ sram_2_16_1_sky130_row_addr_dff +Xdata_dff0 ++ din0[0] din0[1] din0[2] bank_din0_0 bank_din0_1 bank_din0_2 clk_buf0 ++ vccd1 vssd1 ++ sram_2_16_1_sky130_data_dff +Xspare_wen_dff0 ++ spare_wen0[0] bank_spare_wen0_0 clk_buf0 vccd1 vssd1 ++ sram_2_16_1_sky130_spare_wen_dff +.ENDS sram_2_16_1_sky130 diff --git a/docs/source/characterization.md b/docs/source/characterization.md index 60adc74a..5fdde81b 100644 --- a/docs/source/characterization.md +++ b/docs/source/characterization.md @@ -46,7 +46,8 @@ Measures the timing/power through SPICE simulation: * Testing Support Modules * Other modules are derivatives of the simulation module used in the unit tests - +## Stand-alone Charaterizer +The stand-alone characterizer is a script ([sram_char.py]()sram_char.py) that can be run without generating an SRAM. ## Characterization Options * Characterization by Configuration File diff --git a/macros/Makefile b/macros/Makefile index 58b7f09e..b50db045 100644 --- a/macros/Makefile +++ b/macros/Makefile @@ -28,6 +28,8 @@ ROM_SRCS=$(filter-out disabled-% %_common.py,$(sort $(notdir $(wildcard $(ROM_CO ROM_DIRS=$(basename $(ROM_SRCS)) ROM_STAMPS=$(addsuffix .ok,$(ROM_DIRS)) +DIRS=$(SRAM_DIRS) $(ROM_DIRS) + configs: @echo @echo "Using OpenRAM at $(OPENRAM_HOME)" @@ -80,12 +82,12 @@ sram: $(WORKING_SRAM_STAMPS) %.ok: sram_configs/%.py @echo "Building $*" @mkdir -p $* - @python3 -u $(SRAM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ + @OPENRAM_TMP=$*/tmp python3 -u $(SRAM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ %.ok: rom_configs/%.py @echo "Building $*" @mkdir -p $* - @python3 -u $(ROM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ + @OPENRAM_TMP=$*/tmp python3 -u $(ROM_COMPILER) $(OPENRAM_OPTS) -o $* -p $(MACRO_DIR)/$* $(MACRO_DIR)/$< && touch $@ .DELETE_ON_ERROR: $(WORKING_SRAM_STAMPS) $(WORKING_ROM_STAMPS) diff --git a/sram_char.py b/sram_char.py new file mode 100755 index 00000000..52a085cb --- /dev/null +++ b/sram_char.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +This script will characterize an SRAM previously generated by OpenRAM given a +configuration file. Configuration option "use_pex" determines whether extracted +or generated spice is used and option "analytical_delay" determines whether +an analytical model or spice simulation is used for characterization. +""" + +import sys +import datetime + +# You don't need the next two lines if you're sure that openram package is installed +from common import * +make_openram_package() +import openram + +(OPTS, args) = openram.parse_args() + +# Override the usage +USAGE = "Usage: {} [options] \nUse -h for help.\n".format(__file__) + +# Check that we are left with a single configuration file as argument. +if len(args) != 2: + print(USAGE) + sys.exit(2) + +OPTS.top_process = 'memchar' + +# These depend on arguments, so don't load them until now. +from openram import debug + +# Parse config file and set up all the options +openram.init_openram(config_file=args[0], is_unit_test=False) + +openram.print_banner() + +# Configure the SRAM organization (duplicated from openram.py) +from openram.characterizer import fake_sram +s = fake_sram(name=OPTS.output_name, + word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +debug.check(os.path.exists(args[1]), "Spice netlist file {} not found.".format(args[1])) +sp_file = args[1] +s.generate_pins() +s.setup_multiport_constants() + +OPTS.netlist_only = True +OPTS.check_lvsdrc = False +OPTS.nomimal_corner_only = True + +# TODO: remove this after adding trimmed netlist gen to sram run +OPTS.trim_netlist = False + +# Characterize the design +start_time = datetime.datetime.now() +from openram.characterizer import lib +debug.print_raw("LIB: Characterizing... ") +lib(out_dir=OPTS.output_path, sram=s, sp_file=sp_file, use_model=False) +print_time("Characterization", datetime.datetime.now(), start_time) + +# Output info about this run +print("Output files are:\n{0}*.lib".format(OPTS.output_path)) +#report_status() #could modify this function to provide relevant info + +# Delete temp files, remove the dir, etc. +openram.end_openram() diff --git a/sram_compiler.py b/sram_compiler.py index 389418b0..10f32393 100755 --- a/sram_compiler.py +++ b/sram_compiler.py @@ -32,6 +32,8 @@ if len(args) != 1: print(openram.USAGE) sys.exit(2) +# Set top process to openram +OPTS.top_process = 'openram' # These depend on arguments, so don't load them until now. from openram import debug @@ -76,4 +78,3 @@ s.save() # Delete temp files etc. openram.end_openram() openram.print_time("End", datetime.datetime.now(), start_time) - diff --git a/sram_func.py b/sram_func.py new file mode 100755 index 00000000..5b7b1569 --- /dev/null +++ b/sram_func.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +This script will functionally simulate an SRAM previously generated by OpenRAM +given a configuration file. Configuration option "use_pex" determines whether +extracted or generated spice is used. Command line arguments dictate the +number of cycles and period to be simulated. +""" + +import sys +import datetime + +# You don't need the next two lines if you're sure that openram package is installed +from common import * +make_openram_package() +import openram + +(OPTS, args) = openram.parse_args() + +# Override the usage +USAGE = "Usage: {} [options] \nUse -h for help.\n".format(__file__) + +# Check that we are left with a single configuration file as argument. +if len(args) != 4: + print(USAGE) + sys.exit(2) + +OPTS.top_process = 'memfunc' + +# Parse argument +config_file = args[0] +sp_file = args[1] +cycles = int(args[2]) +period = float(args[3]) + +# These depend on arguments, so don't load them until now. +from openram import debug + +# Parse config file and set up all the options +openram.init_openram(config_file=config_file, is_unit_test=False) + +openram.print_banner() + +# Configure the SRAM organization (duplicated from openram.py) +from openram.characterizer.fake_sram import fake_sram +s = fake_sram(name=OPTS.output_name, + word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +s.generate_pins() +s.setup_multiport_constants() + +OPTS.netlist_only = True +OPTS.check_lvsdrc = False + +# Generate stimulus and run functional simulation on the design +start_time = datetime.datetime.now() +from openram.characterizer import functional +debug.print_raw("Functional simulation... ") +f = functional(s, cycles=cycles, spfile=sp_file, period=period, output_path=OPTS.openram_temp) +(fail, error) = f.run() +debug.print_raw(error) +openram.print_time("Functional simulation", datetime.datetime.now(), start_time) + +# Delete temp files, remove the dir, etc. after success +if fail: + openram.end_openram() diff --git a/technology/freepdk45/gds_lib/sense_amp.gds b/technology/freepdk45/gds_lib/sense_amp.gds index f0bfebfd..51513e13 100644 Binary files a/technology/freepdk45/gds_lib/sense_amp.gds and b/technology/freepdk45/gds_lib/sense_amp.gds differ diff --git a/technology/freepdk45/sp_lib/sense_amp.sp b/technology/freepdk45/sp_lib/sense_amp.sp index b5778306..8daf8a00 100644 --- a/technology/freepdk45/sp_lib/sense_amp.sp +++ b/technology/freepdk45/sp_lib/sense_amp.sp @@ -1,11 +1,16 @@ .SUBCKT sense_amp bl br dout en vdd gnd -M_1 dout net_1 vdd vdd pmos_vtg w=540.0n l=50.0n -M_3 net_1 dout vdd vdd pmos_vtg w=540.0n l=50.0n -M_2 dout net_1 net_2 gnd nmos_vtg w=270.0n l=50.0n -M_8 net_1 dout net_2 gnd nmos_vtg w=270.0n l=50.0n -M_5 bl en dout vdd pmos_vtg w=720.0n l=50.0n +M_1 dint net_1 vdd vdd pmos_vtg w=540.0n l=50.0n +M_3 net_1 dint vdd vdd pmos_vtg w=540.0n l=50.0n +M_2 dint net_1 net_2 gnd nmos_vtg w=270.0n l=50.0n +M_8 net_1 dint net_2 gnd nmos_vtg w=270.0n l=50.0n +M_5 bl en dint vdd pmos_vtg w=720.0n l=50.0n M_6 br en net_1 vdd pmos_vtg w=720.0n l=50.0n M_7 net_2 en gnd gnd nmos_vtg w=270.0n l=50.0n + +M_9 dout_bar dint vdd vdd pmos_vtg w=180.0n l=50.0n +M_10 dout_bar dint gnd gnd nmos_vtg w=90.0n l=50.0n +M_11 dout dout_bar vdd vdd pmos_vtg w=540.0n l=50.0n +M_12 dout dout_bar gnd gnd nmos_vtg w=270.0n l=50.0n .ENDS sense_amp diff --git a/technology/scn4m_subm/gds_lib/sense_amp.gds b/technology/scn4m_subm/gds_lib/sense_amp.gds index 5ffbb0d5..5790e4bc 100644 Binary files a/technology/scn4m_subm/gds_lib/sense_amp.gds and b/technology/scn4m_subm/gds_lib/sense_amp.gds differ diff --git a/technology/scn4m_subm/mag_lib/sense_amp.mag b/technology/scn4m_subm/mag_lib/sense_amp.mag index e5fa4373..08e20f6d 100644 --- a/technology/scn4m_subm/mag_lib/sense_amp.mag +++ b/technology/scn4m_subm/mag_lib/sense_amp.mag @@ -1,136 +1,183 @@ magic tech scmos -timestamp 1536089670 +timestamp 1681333912 << nwell >> -rect 0 0 40 102 +rect 0 28 40 153 << pwell >> -rect 0 102 40 163 +rect 0 153 40 214 +rect 0 0 40 28 << ntransistor >> -rect 21 130 23 139 -rect 12 108 14 117 -rect 20 108 22 117 +rect 21 181 23 190 +rect 12 159 14 168 +rect 20 159 22 168 +rect 13 10 15 22 +rect 21 18 23 22 << ptransistor >> -rect 12 78 14 96 -rect 20 78 22 96 -rect 11 20 13 44 -rect 27 20 29 44 +rect 12 129 14 147 +rect 20 129 22 147 +rect 11 71 13 95 +rect 27 71 29 95 +rect 13 34 15 58 +rect 21 34 23 42 << ndiffusion >> -rect 20 130 21 139 -rect 23 130 24 139 -rect 11 108 12 117 -rect 14 108 15 117 -rect 19 108 20 117 -rect 22 108 23 117 +rect 20 181 21 190 +rect 23 181 24 190 +rect 11 159 12 168 +rect 14 159 15 168 +rect 19 159 20 168 +rect 22 159 23 168 +rect 12 10 13 22 +rect 15 10 16 22 +rect 20 18 21 22 +rect 23 18 24 22 << pdiffusion >> -rect 7 94 12 96 -rect 11 80 12 94 -rect 7 78 12 80 -rect 14 94 20 96 -rect 14 80 15 94 -rect 19 80 20 94 -rect 14 78 20 80 -rect 22 94 27 96 -rect 22 80 23 94 -rect 22 78 27 80 -rect 10 20 11 44 -rect 13 20 14 44 -rect 26 20 27 44 -rect 29 20 30 44 +rect 7 145 12 147 +rect 11 131 12 145 +rect 7 129 12 131 +rect 14 145 20 147 +rect 14 131 15 145 +rect 19 131 20 145 +rect 14 129 20 131 +rect 22 145 27 147 +rect 22 131 23 145 +rect 22 129 27 131 +rect 10 71 11 95 +rect 13 71 14 95 +rect 26 71 27 95 +rect 29 71 30 95 +rect 12 34 13 58 +rect 15 34 16 58 +rect 20 34 21 42 +rect 23 34 24 42 << ndcontact >> -rect 16 130 20 139 -rect 24 130 28 139 -rect 7 108 11 117 -rect 15 108 19 117 -rect 23 108 27 117 +rect 16 181 20 190 +rect 24 181 28 190 +rect 7 159 11 168 +rect 15 159 19 168 +rect 23 159 27 168 +rect 8 10 12 22 +rect 16 10 20 22 +rect 24 18 28 22 << pdcontact >> -rect 7 80 11 94 -rect 15 80 19 94 -rect 23 80 27 94 -rect 6 20 10 44 -rect 14 20 18 44 -rect 22 20 26 44 -rect 30 20 34 44 +rect 7 131 11 145 +rect 15 131 19 145 +rect 23 131 27 145 +rect 6 71 10 95 +rect 14 71 18 95 +rect 22 71 26 95 +rect 30 71 34 95 +rect 8 34 12 58 +rect 16 34 20 58 +rect 24 34 28 42 << psubstratepcontact >> -rect 32 137 36 141 +rect 32 188 36 192 +rect 32 13 36 17 << nsubstratencontact >> -rect 27 70 31 74 +rect 27 121 31 125 +rect 27 55 31 59 << polysilicon >> -rect 21 139 23 149 -rect 21 129 23 130 -rect 3 127 23 129 -rect 3 47 5 127 -rect 12 122 34 124 -rect 12 117 14 122 -rect 20 117 22 119 -rect 12 96 14 108 -rect 20 96 22 108 -rect 32 105 34 122 -rect 30 101 34 105 -rect 12 76 14 78 -rect 20 69 22 78 -rect 13 67 22 69 -rect 9 55 11 65 -rect 32 55 34 101 -rect 33 51 34 55 -rect 3 45 13 47 -rect 11 44 13 45 -rect 27 44 29 46 -rect 11 19 13 20 -rect 27 19 29 20 -rect 11 17 29 19 +rect 21 190 23 200 +rect 21 180 23 181 +rect 3 178 23 180 +rect 3 98 5 178 +rect 12 173 34 175 +rect 12 168 14 173 +rect 20 168 22 170 +rect 12 147 14 159 +rect 20 147 22 159 +rect 32 156 34 173 +rect 30 152 34 156 +rect 12 127 14 129 +rect 20 120 22 129 +rect 13 118 22 120 +rect 9 106 11 116 +rect 32 106 34 152 +rect 33 102 34 106 +rect 3 96 13 98 +rect 11 95 13 96 +rect 27 95 29 97 +rect 11 70 13 71 +rect 27 70 29 71 +rect 11 68 29 70 +rect 7 63 23 65 +rect 13 58 15 60 +rect 21 42 23 63 +rect 13 31 15 34 +rect 13 27 14 31 +rect 13 22 15 27 +rect 21 22 23 34 +rect 21 16 23 18 +rect 13 8 15 10 << polycontact >> -rect 20 149 24 153 -rect 26 101 30 105 -rect 9 65 13 69 -rect 9 51 13 55 -rect 29 51 33 55 +rect 20 200 24 204 +rect 26 152 30 156 +rect 9 116 13 120 +rect 9 102 13 106 +rect 29 102 33 106 +rect 3 63 7 67 +rect 14 27 18 31 << metal1 >> -rect -2 149 20 153 -rect 24 149 36 153 -rect 28 133 32 137 -rect 16 117 19 130 -rect 7 94 11 108 -rect 23 105 27 108 -rect 23 101 26 105 -rect 7 69 11 80 -rect 15 94 19 96 -rect 15 78 19 80 -rect 23 94 27 101 -rect 23 78 27 80 -rect 15 75 18 78 -rect 15 74 31 75 -rect 15 72 27 74 -rect 7 65 9 69 -rect 6 44 9 54 -rect 33 51 34 55 -rect 31 44 34 51 -rect 3 20 6 23 -rect 3 15 7 20 +rect -2 200 20 204 +rect 24 200 36 204 +rect 28 184 32 188 +rect 16 168 19 181 +rect 7 145 11 159 +rect 23 156 27 159 +rect 23 152 26 156 +rect 7 120 11 131 +rect 15 145 19 147 +rect 15 129 19 131 +rect 23 145 27 152 +rect 23 129 27 131 +rect 15 126 18 129 +rect 15 125 31 126 +rect 15 123 27 125 +rect 7 116 9 120 +rect 6 95 9 105 +rect 33 102 34 106 +rect 31 95 34 102 +rect 3 71 6 74 +rect 3 67 7 71 +rect 20 55 27 58 +rect 8 22 11 34 +rect 24 30 28 34 +rect 18 27 28 30 +rect 24 22 28 27 +rect 20 13 32 15 +rect 20 12 36 13 +rect 8 8 11 10 +rect 7 5 11 8 << m2contact >> -rect 32 133 36 137 -rect 27 66 31 70 -rect 13 44 17 48 -rect 22 44 26 48 -rect 3 11 7 15 +rect 32 184 36 188 +rect 27 117 31 121 +rect 13 95 17 99 +rect 22 95 26 99 +rect 27 51 31 55 +rect 32 17 36 21 +rect 3 4 7 8 << metal2 >> -rect 10 48 14 163 -rect 20 48 24 163 -rect 32 129 36 133 -rect 27 62 31 66 -rect 10 44 13 48 -rect 20 44 22 48 -rect 3 0 7 11 -rect 10 0 14 44 -rect 20 0 24 44 +rect 10 99 14 214 +rect 20 99 24 214 +rect 32 180 36 184 +rect 27 113 31 117 +rect 10 95 13 99 +rect 20 95 22 99 +rect 3 0 7 4 +rect 10 0 14 95 +rect 20 0 24 95 +rect 27 47 31 51 +rect 32 21 36 25 << bb >> -rect 0 0 34 163 +rect 0 0 34 214 << labels >> -flabel metal1 0 149 0 149 4 FreeSans 26 0 0 0 en -rlabel metal2 34 131 34 131 1 gnd -rlabel metal2 29 64 29 64 1 vdd -rlabel metal2 12 161 12 161 5 bl -rlabel metal2 22 161 22 161 5 br -rlabel metal2 5 3 5 3 1 dout +rlabel metal2 5 2 5 2 1 dout +rlabel metal2 29 49 29 49 1 vdd +rlabel metal2 22 212 22 212 5 br +rlabel metal2 12 212 12 212 5 bl +rlabel metal2 29 115 29 115 1 vdd +rlabel metal2 34 182 34 182 1 gnd +flabel metal1 0 200 0 200 4 FreeSans 26 0 0 0 en +rlabel metal2 34 23 34 23 1 gnd << properties >> string path 270.000 468.000 270.000 486.000 288.000 486.000 288.000 468.000 270.000 468.000 << end >> diff --git a/technology/scn4m_subm/sp_lib/sense_amp.sp b/technology/scn4m_subm/sp_lib/sense_amp.sp index 70622413..2b72262e 100644 --- a/technology/scn4m_subm/sp_lib/sense_amp.sp +++ b/technology/scn4m_subm/sp_lib/sense_amp.sp @@ -5,11 +5,15 @@ * SPICE3 file created from sense_amp.ext - technology: scmos M1000 gnd en a_56_432# gnd n w=1.8u l=0.4u -M1001 a_56_432# a_48_304# dout gnd n w=1.8u l=0.4u -M1002 a_48_304# dout a_56_432# gnd n w=1.8u l=0.4u -M1003 vdd a_48_304# dout vdd p w=3.6u l=0.4u -M1004 a_48_304# dout vdd vdd p w=3.6u l=0.4u -M1005 bl en dout vdd p w=4.8u l=0.4u +M1001 a_56_432# a_48_304# dint gnd n w=1.8u l=0.4u +M1002 a_48_304# dint a_56_432# gnd n w=1.8u l=0.4u +M1003 vdd a_48_304# dint vdd p w=3.6u l=0.4u +M1004 a_48_304# dint vdd vdd p w=3.6u l=0.4u +M1005 bl en dint vdd p w=4.8u l=0.4u M1006 a_48_304# en br vdd p w=4.8u l=0.4u +M1007 dout_bar dint vdd vdd p w=1.6u l=0.4u +M1008 gnd dint dout_bar gnd n w=0.8u l=0.4u +M1009 dout dout_bar vdd vdd p w=4.8u l=0.4u +M1010 gnd dout_bar dout gnd n w=2.4u l=0.4u .ENDS