diff --git a/compiler/base/hierarchy_design.py b/compiler/base/hierarchy_design.py index e4c77987..bb57ae3e 100644 --- a/compiler/base/hierarchy_design.py +++ b/compiler/base/hierarchy_design.py @@ -7,15 +7,11 @@ # import hierarchy_layout import hierarchy_spice -import globals import verify import debug import os from globals import OPTS -import graph_util -total_drc_errors = 0 -total_lvs_errors = 0 class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ @@ -28,120 +24,157 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" + # If we have a separate lvs directory, then all the lvs files + # should be in there (all or nothing!) + lvs_dir = OPTS.openram_tech + "lvs_lib/" + if os.path.exists(lvs_dir): + self.lvs_file = lvs_dir + name + ".sp" + else: + self.lvs_file = self.sp_file + self.name = name hierarchy_spice.spice.__init__(self, name) hierarchy_layout.layout.__init__(self, name) self.init_graph_params() - def get_layout_pins(self,inst): + def get_layout_pins(self, inst): """ Return a map of pin locations of the instance offset """ # find the instance for i in self.insts: if i.name == inst.name: break else: - debug.error("Couldn't find instance {0}".format(inst_name),-1) + debug.error("Couldn't find instance {0}".format(inst_name), -1) inst_map = inst.mod.pin_map return inst_map - - def DRC_LVS(self, final_verification=False, top_level=False): + def DRC_LVS(self, final_verification=False, force_check=False): """Checks both DRC and LVS for a module""" - - # Final verification option does not allow nets to be connected by label. + + # No layout to check + if OPTS.netlist_only: + return ("skipped", "skipped") # Unit tests will check themselves. - if OPTS.is_unit_test: - return - if not OPTS.check_lvsdrc: - return + if not force_check and OPTS.is_unit_test: + return ("skipped", "skipped") + if not force_check and not OPTS.check_lvsdrc: + return ("skipped", "skipped") # Do not run if disabled in options. - if (OPTS.inline_lvsdrc or top_level): + if (OPTS.inline_lvsdrc or force_check or final_verification): - global total_drc_errors - global total_lvs_errors - tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) - self.sp_write(tempspice) + tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) + self.lvs_write(tempspice) self.gds_write(tempgds) + # Final verification option does not allow nets to be connected by label. + num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) + num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification) - num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) - debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) - total_drc_errors += num_drc_errors - total_lvs_errors += num_lvs_errors + # force_check is used to determine decoder height and other things, so we shouldn't fail + # if that flag is set + if OPTS.inline_lvsdrc and not force_check: + debug.check(num_drc_errors == 0, + "DRC failed for {0} with {1} error(s)".format(self.name, + num_drc_errors)) + debug.check(num_lvs_errors == 0, + "LVS failed for {0} with {1} errors(s)".format(self.name, + num_lvs_errors)) os.remove(tempspice) os.remove(tempgds) + + return (num_drc_errors, num_lvs_errors) + else: + return ("skipped", "skipped") def DRC(self, final_verification=False): """Checks DRC for a module""" # Unit tests will check themselves. # Do not run if disabled in options. + # No layout to check + if OPTS.netlist_only: + return "skipped" + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - global total_drc_errors - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.gds_write(tempgds) - num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) - total_drc_errors += num_errors - debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error)) + num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification) + debug.check(num_errors == 0, + "DRC failed for {0} with {1} error(s)".format(self.name, + num_errors)) os.remove(tempgds) + return num_errors + else: + return "skipped" + def LVS(self, final_verification=False): """Checks LVS for a module""" # Unit tests will check themselves. # Do not run if disabled in options. + # No layout to check + if OPTS.netlist_only: + return "skipped" + if (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): - global total_lvs_errors - tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) - tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) - self.sp_write(tempspice) + tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) + tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) + self.lvs_write(tempspice) self.gds_write(tempgds) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) - total_lvs_errors += num_errors - debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors)) + debug.check(num_errors == 0, + "LVS failed for {0} with {1} error(s)".format(self.name, + num_errors)) os.remove(tempspice) - os.remove(tempgds) + os.remove(tempgds) + + return num_errors + else: + return "skipped" def init_graph_params(self): """Initializes parameters relevant to the graph creation""" - #Only initializes a set for checking instances which should not be added + # Only initializes a set for checking instances which should not be added self.graph_inst_exclude = set() - def build_graph(self, graph, inst_name, port_nets): + def build_graph(self, graph, inst_name, port_nets): """Recursively create graph from instances in module.""" - #Translate port names to external nets + # 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),1) - port_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, + self.pins), + 1) + port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) for subinst, conns in zip(self.insts, self.conns): if subinst in self.graph_inst_exclude: continue - subinst_name = inst_name+'.X'+subinst.name + subinst_name = inst_name + '.X' + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) subinst.mod.build_graph(graph, subinst_name, subinst_ports) def build_names(self, name_dict, inst_name, port_nets): """Collects all the nets and the parent inst of that net.""" - #Translate port names to external nets + # 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),1) - port_dict = {pin:port for pin,port in zip(self.pins, port_nets)} + debug.error("Port length mismatch:\nExt nets={}, Ports={}".format(port_nets, + self.pins), + 1) + port_dict = {pin: port for pin, port in zip(self.pins, port_nets)} debug.info(3, "Instance name={}".format(inst_name)) for subinst, conns in zip(self.insts, self.conns): - subinst_name = inst_name+'.X'+subinst.name + subinst_name = inst_name + '.X' + subinst.name subinst_ports = self.translate_nets(conns, port_dict, inst_name) for si_port, conn in zip(subinst_ports, conns): - #Only add for first occurrence + # Only add for first occurrence if si_port.lower() not in name_dict: - mod_info = {'mod':self, 'int_net':conn} + mod_info = {'mod': self, 'int_net': conn} name_dict[si_port.lower()] = mod_info - subinst.mod.build_names(name_dict, subinst_name, subinst_ports) + subinst.mod.build_names(name_dict, subinst_name, subinst_ports) def find_aliases(self, inst_name, port_nets, path_nets, alias, alias_mod, exclusion_set=None): """Given a list of nets, will compare the internal alias of a mod to determine @@ -161,17 +194,17 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): int_mod = self.name_dict[net]['mod'] if int_mod.is_net_alias(int_net, alias, alias_mod, exclusion_set): aliases.append(net) - return aliases + return aliases 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).""" if self in exclusion_set: return False - #Check ports of this mod + # Check ports of this mod for pin in self.pins: if self.is_net_alias_name_check(known_net, pin, net_alias, mod): return True - #Check connections of all other subinsts + # Check connections of all other subinsts mod_set = set() for subinst, inst_conns in zip(self.insts, self.conns): for inst_conn, mod_pin in zip(inst_conns, subinst.mod.pins): @@ -181,7 +214,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): if subinst.mod.is_net_alias(mod_pin, net_alias, mod, exclusion_set): return True mod_set.add(subinst.mod) - return False + return False def is_net_alias_name_check(self, parent_net, child_net, alias_net, mod): """Utility function for checking single net alias.""" @@ -190,8 +223,10 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): parent_net.lower() == alias_net.lower() def get_mod_net(self, parent_net, child_inst, child_conns): - """Given an instance and net, returns the internal net in the mod - corresponding to input net.""" + """ + Given an instance and net, returns the internal net in the mod + corresponding to input net. + """ for conn, pin in zip(child_conns, child_inst.mod.pins): if parent_net.lower() == conn.lower(): return pin @@ -205,27 +240,27 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): converted_conns.append(port_dict[conn]) else: converted_conns.append("{}.{}".format(inst_name, conn)) - return converted_conns + return converted_conns def add_graph_edges(self, graph, port_nets): """For every input, adds an edge to every output. Only intended to be used for gates and other simple modules.""" - #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)} + # 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)} input_pins = self.get_inputs() output_pins = self.get_outputs() inout_pins = self.get_inouts() - for inp in input_pins+inout_pins: - for out in output_pins+inout_pins: - if inp != out: #do not add self loops - graph.add_edge(pin_dict[inp], pin_dict[out], self) + for inp in input_pins + inout_pins: + for out in output_pins + inout_pins: + if inp != out: # do not add self loops + graph.add_edge(pin_dict[inp], pin_dict[out], self) def __str__(self): """ override print function output """ pins = ",".join(self.pins) insts = [" {}".format(x) for x in self.insts] - objs = [" {}".format(x) for x in self.objs] + objs = [" {}".format(x) for x in self.objs] s = "********** design {0} **********".format(self.name) s += "\n pins ({0})={1}\n".format(len(self.pins), pins) s += "\n objs ({0})=\n{1}\n".format(len(self.objs), "\n".join(objs)) @@ -236,8 +271,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout): """ override print function output """ text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n" for i in self.objs: - text+=str(i)+",\n" + text+=str(i) + ",\n" for i in self.insts: - text+=str(i)+",\n" + text+=str(i) + ",\n" return text diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index 707e7bc8..8091af63 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -15,6 +15,7 @@ from wire_spice_model import * from power_data import * import logical_effort + class spice(): """ This provides a set of useful generic types for hierarchy @@ -30,19 +31,19 @@ class spice(): self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "POWER", "GROUND"] # Holds subckts/mods for this module - self.mods = [] + self.mods = [] # Holds the pins for this module 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 = {} + self.pin_type = {} # THE CONNECTIONS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # Spice format) self.conns = [] # Keep track of any comments to add the the spice try: self.commments - except: + except AttributeError: self.comments = [] self.sp_read() @@ -56,7 +57,7 @@ class spice(): try: self.commments - except: + except AttributeError: self.comments = [] self.comments.append(comment) @@ -65,7 +66,9 @@ class spice(): """ 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(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(name, + pin_type)) def add_pin_list(self, pin_list, pin_type="INOUT"): """ Adds a pin_list to the pins list """ @@ -73,36 +76,43 @@ class spice(): # or a list that is the same length as the pin list. if type(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) + 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, 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) else: debug.error("Mismatch in type and pin list lengths.", -1) def add_pin_types(self, type_list): - """Add pin types for all the cell's pins. - Typically, should only be used for handmade cells.""" - #This only works if self.pins == bitcell.pin_names + """ + Add pin types for all the cell's pins. + Typically, should only be used for handmade cells. + """ + # This only works if self.pins == bitcell.pin_names if self.pin_names != self.pins: debug.error("{} spice subcircuit port names do not match pin_names\ \n SPICE names={}\ \n Module names={}\ - ".format(self.name, self.pin_names, self.pins),1) - self.pin_type = {pin:type for pin,type in zip(self.pin_names, type_list)} + ".format(self.name, self.pin_names, self.pins), 1) + self.pin_type = {pin: type for pin, type in zip(self.pin_names, type_list)} 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)) + debug.check(pin_type in self.valid_signal_types, + "Invalid signaltype for {0}: {1}".format(name, pin_type)) 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"]: + if self.pin_type[name] in ["POWER", "GROUND"]: return "INOUT" else: return self.pin_type[name] @@ -125,11 +135,10 @@ class spice(): output_list.append(pin) return output_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)) + self.add_pin(pin + suffix, other_module.get_pin_type(pin)) def get_inouts(self): """ These use pin types to determine pin lists. These @@ -144,7 +153,6 @@ class spice(): """Adds a subckt/submodule to the subckt hierarchy""" self.mods.append(mod) - def connect_inst(self, args, check=True): """Connects the pins of the last instance added It is preferred to use the function with the check to find if @@ -169,21 +177,23 @@ class spice(): 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("Instances: \n" + str(insts_string)) debug.error("-----") - debug.error("Connections: \n"+str(conns_string),1) + 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 + # If not found, returns None return None def sp_read(self): - """Reads the sp file (and parse the pins) from the library - Otherwise, initialize it to null for dynamic generation""" + """ + Reads the sp file (and parse the pins) from the library + Otherwise, initialize it to null for dynamic generation + """ if self.sp_file and os.path.isfile(self.sp_file): debug.info(3, "opening {0}".format(self.sp_file)) f = open(self.sp_file) @@ -200,15 +210,34 @@ class spice(): else: self.spice = [] + # We don't define self.lvs and will use self.spice if dynamically created + # or they are the same file + if self.lvs_file!=self.sp_file and os.path.isfile(self.lvs_file): + debug.info(3, "opening {0}".format(self.lvs_file)) + f = open(self.lvs_file) + self.lvs = f.readlines() + for i in range(len(self.lvs)): + self.lvs[i] = self.lvs[i].rstrip(" \n") + f.close() + + # pins and subckt should be the same + # find the correct subckt line in the file + subckt = re.compile("^.subckt {}".format(self.name), re.IGNORECASE) + 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, "LVS and spice file pin mismatch.") + def check_net_in_spice(self, net_name): """Checks if a net name exists in the current. Intended to be check nets in hand-made cells.""" - #Remove spaces and lower case then add spaces. Nets are separated by spaces. - net_formatted = ' '+net_name.lstrip().rstrip().lower()+' ' + # Remove spaces and lower case then add spaces. + # Nets are separated by spaces. + net_formatted = ' ' + net_name.lstrip().rstrip().lower() + ' ' for line in self.spice: - #Lowercase the line and remove any part of the line that is a comment. + # Lowercase the line and remove any part of the line that is a comment. line = line.lower().split('*')[0] - #Skip .subckt or .ENDS lines + # Skip .subckt or .ENDS lines if line.find('.') == 0: continue if net_formatted in line: @@ -220,7 +249,7 @@ class spice(): nets_match = True for net in nets: nets_match = nets_match and self.check_net_in_spice(net) - return nets_match + return nets_match def contains(self, mod, modlist): for x in modlist: @@ -228,54 +257,56 @@ class spice(): return True return False - def sp_write_file(self, sp, usedMODS): - """ Recursive spice subcircuit write; - Writes the spice subcircuit from the library or the dynamically generated one""" + def sp_write_file(self, sp, usedMODS, lvs_netlist=False): + """ + Recursive spice subcircuit write; + Writes the spice subcircuit from the library or the dynamically generated one + """ if not self.spice: # recursively write the modules for i in self.mods: if self.contains(i, usedMODS): continue usedMODS.append(i) - i.sp_write_file(sp, usedMODS) + i.sp_write_file(sp, usedMODS, lvs_netlist) if len(self.insts) == 0: return if self.pins == []: return - # write out the first spice line (the subcircuit) sp.write("\n.SUBCKT {0} {1}\n".format(self.name, " ".join(self.pins))) for pin in self.pins: - sp.write("* {1:6}: {0} \n".format(pin,self.pin_type[pin])) + sp.write("* {1:6}: {0} \n".format(pin, self.pin_type[pin])) 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): + if len(self.insts) != len(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(self.insts)) + debug.error("Instances: \n" + str(self.insts)) debug.error("-----") - debug.error("Connections: \n"+str(self.conns),1) - - + debug.error("Connections: \n" + str(self.conns), 1) for i in range(len(self.insts)): # we don't need to output connections of empty instances. # these are wires and paths if self.conns[i] == []: continue - if hasattr(self.insts[i].mod,"spice_device"): + if lvs_netlist 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]))) + 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]))) sp.write("\n") - else: sp.write("X{0} {1} {2}\n".format(self.insts[i].name, " ".join(self.conns[i]), @@ -286,9 +317,12 @@ class spice(): else: # write the subcircuit itself # Including the file path makes the unit test fail for other users. - #if os.path.isfile(self.sp_file): + # if os.path.isfile(self.sp_file): # sp.write("\n* {0}\n".format(self.sp_file)) - sp.write("\n".join(self.spice)) + if lvs_netlist and hasattr(self, "lvs"): + sp.write("\n".join(self.lvs)) + else: + sp.write("\n".join(self.spice)) sp.write("\n") @@ -302,10 +336,21 @@ class spice(): del usedMODS spfile.close() + def lvs_write(self, spname): + """Writes the lvs to files""" + debug.info(3, "Writing to {0}".format(spname)) + spfile = open(spname, 'w') + spfile.write("*FIRST LINE IS A COMMENT\n") + usedMODS = list() + self.sp_write_file(spfile, usedMODS, True) + del usedMODS + spfile.close() + def analytical_delay(self, corner, slew, load=0.0): """Inform users undefined delay module while building new modules""" - # FIXME: Slew is not used in the model right now. Can be added heuristically as linear factor + # FIXME: Slew is not used in the model right now. + # Can be added heuristically as linear factor relative_cap = logical_effort.convert_farad_to_relative_c(load) stage_effort = self.get_stage_effort(relative_cap) @@ -316,7 +361,7 @@ class spice(): abs_delay = stage_effort.get_absolute_delay() corner_delay = self.apply_corners_analytically(abs_delay, corner) SLEW_APPROXIMATION = 0.1 - corner_slew = SLEW_APPROXIMATION*corner_delay + corner_slew = SLEW_APPROXIMATION * corner_delay return delay_data(corner_delay, corner_slew) def get_stage_effort(self, cout, inp_is_rise=True): @@ -326,7 +371,7 @@ class spice(): debug.warning("Class {0} name {1}" .format(self.__class__.__name__, self.name)) - return None + return None def get_cin(self): """Returns input load in Femto-Farads. All values generated using @@ -342,35 +387,35 @@ class spice(): debug.warning("Design Class {0} input capacitance function needs to be defined" .format(self.__class__.__name__)) debug.warning("Class {0} name {1}" - .format(self.__class__.__name__, - self.name)) - return 0 + .format(self.__class__.__name__, + self.name)) + return 0 - def cal_delay_with_rc(self, corner, r, c ,slew, swing = 0.5): - """ - Calculate the delay of a mosfet by + def cal_delay_with_rc(self, corner, r, c, slew, swing=0.5): + """ + Calculate the delay of a mosfet by modeling it as a resistance driving a capacitance """ - swing_factor = abs(math.log(1-swing)) # time constant based on swing - delay = swing_factor * r * c #c is in ff and delay is in fs + swing_factor = abs(math.log(1 - swing)) # time constant based on swing + delay = swing_factor * r * c # c is in ff and delay is in fs delay = self.apply_corners_analytically(delay, corner) - delay = delay * 0.001 #make the unit to ps + delay = delay * 0.001 # make the unit to ps - # Output slew should be linear to input slew which is described + # Output slew should be linear to input slew which is described # as 0.005* slew. # The slew will be also influenced by the delay. - # If no input slew(or too small to make impact) - # The mimum slew should be the time to charge RC. + # If no input slew(or too small to make impact) + # The mimum slew should be the time to charge RC. # Delay * 2 is from 0 to 100% swing. 0.6*2*delay is from 20%-80%. slew = delay * 0.6 * 2 + 0.005 * slew - return delay_data(delay = delay, slew = slew) + return delay_data(delay=delay, slew=slew) def apply_corners_analytically(self, delay, corner): """Multiply delay by corner factors""" - proc,vdd,temp = corner - #FIXME: type of delay is needed to know which process to use. - proc_mult = max(self.get_process_delay_factor(proc)) + proc, vdd, temp = corner + # FIXME: type of delay is needed to know which process to use. + proc_mult = max(self.get_process_delay_factor(proc)) volt_mult = self.get_voltage_delay_factor(vdd) temp_mult = self.get_temp_delay_factor(temp) return delay * proc_mult * volt_mult * temp_mult @@ -385,48 +430,51 @@ class spice(): elif mos_proc == 'F': proc_factors.append(0.9) elif mos_proc == 'S': - proc_factors.append(1.1) + proc_factors.append(1.1) return proc_factors def get_voltage_delay_factor(self, voltage): """Returns delay increase due to voltage. Implemented as linear factor based off nominal voltage. """ - return tech.spice["nom_supply_voltage"]/voltage + return tech.spice["nom_supply_voltage"] / voltage def get_temp_delay_factor(self, temp): """Returns delay increase due to temperature (in C). Determines effect on threshold voltage and then linear factor is estimated. """ - #Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV - #(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V - thermal_voltage_nom = 0.008625*tech.spice["nom_temperature"] - thermal_voltage = 0.008625*temp - vthresh = (tech.spice["nom_threshold"]+2*(thermal_voltage-thermal_voltage_nom)) - #Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated. - return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"])/(tech.spice["nom_supply_voltage"]-vthresh) + # Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV + # (k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V + thermal_voltage_nom = 0.008625 * tech.spice["nom_temperature"] + thermal_voltage = 0.008625 * temp + vthresh = (tech.spice["nom_threshold"] + 2 * (thermal_voltage - thermal_voltage_nom)) + # Calculate effect on Vdd-Vth. + # The current vdd is not used here. + # A separate vdd factor is calculated. + return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"]) / (tech.spice["nom_supply_voltage"] - vthresh) def return_delay(self, delay, slew): return delay_data(delay, slew) - def generate_rc_net(self,lump_num, wire_length, wire_width): + def generate_rc_net(self, lump_num, wire_length, wire_width): return wire_spice_model(lump_num, wire_length, wire_width) def calc_dynamic_power(self, corner, c, freq, swing=1.0): - """ + """ Calculate dynamic power using effective capacitance, frequency, and corner (PVT) """ - proc,vdd,temp = corner - net_vswing = vdd*swing - power_dyn = c*vdd*net_vswing*freq + proc, vdd, temp = corner + net_vswing = vdd * swing + power_dyn = c * vdd * net_vswing * freq - #Apply process and temperature factors. Roughly, process and Vdd affect the delay which affects the power. - #No other estimations are currently used. Increased delay->slower freq.->less power - proc_div = max(self.get_process_delay_factor(proc)) + # A pply process and temperature factors. + # Roughly, process and Vdd affect the delay which affects the power. + # No other estimations are currently used. Increased delay->slower freq.->less power + proc_div = max(self.get_process_delay_factor(proc)) temp_div = self.get_temp_delay_factor(temp) - power_dyn = power_dyn/(proc_div*temp_div) + power_dyn = power_dyn / (proc_div * temp_div) - return power_dyn + return power_dyn def return_power(self, dynamic=0.0, leakage=0.0): return power_data(dynamic, leakage) diff --git a/compiler/bitcells/pbitcell.py b/compiler/bitcells/pbitcell.py index 61bd9710..be96a129 100644 --- a/compiler/bitcells/pbitcell.py +++ b/compiler/bitcells/pbitcell.py @@ -7,7 +7,7 @@ # import contact import debug -from tech import drc, parameter +from tech import drc, parameter, layer from vector import vector from ptx import ptx from globals import OPTS @@ -975,42 +975,44 @@ class pbitcell(bitcell_base.bitcell_base): """ Connects wells between ptx modules and places well contacts """ - # extend pwell to encompass entire nmos region of the cell up to the - # height of the tallest nmos transistor - max_nmos_well_height = max(self.inverter_nmos.well_height, - self.readwrite_nmos.well_height, - self.write_nmos.well_height, - self.read_nmos.well_height) - well_height = max_nmos_well_height + self.port_ypos \ - - self.nwell_enclose_active - self.gnd_position.y - # FIXME fudge factor xpos - well_width = self.width + 2*self.nwell_enclose_active - offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos) - self.add_rect(layer="pwell", - offset=offset, - width=well_width, - height=well_height) - + if "pwell" in layer: + # extend pwell to encompass entire nmos region of the cell up to the + # height of the tallest nmos transistor + max_nmos_well_height = max(self.inverter_nmos.well_height, + self.readwrite_nmos.well_height, + self.write_nmos.well_height, + self.read_nmos.well_height) + well_height = max_nmos_well_height + self.port_ypos \ + - self.nwell_enclose_active - self.gnd_position.y + # FIXME fudge factor xpos + well_width = self.width + 2*self.nwell_enclose_active + offset = vector(self.leftmost_xpos - self.nwell_enclose_active, self.botmost_ypos) + self.add_rect(layer="pwell", + offset=offset, + width=well_width, + height=well_height) + # extend nwell to encompass inverter_pmos # calculate offset of the left pmos well - inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ - - self.nwell_enclose_active - inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ - + self.inverter_gap - self.nwell_enclose_active + if "nwell" in layer: + inverter_well_xpos = -(self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ + - self.nwell_enclose_active + inverter_well_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height \ + + self.inverter_gap - self.nwell_enclose_active + + # calculate width of the two combined nwells + # calculate height to encompass nimplant connected to vdd + well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ + + 2 * self.nwell_enclose_active + well_height = self.vdd_position.y - inverter_well_ypos \ + + self.nwell_enclose_active + drc["minwidth_tx"] - # calculate width of the two combined nwells - # calculate height to encompass nimplant connected to vdd - well_width = 2 * (self.inverter_nmos.active_width + 0.5 * self.inverter_to_inverter_spacing) \ - + 2 * self.nwell_enclose_active - well_height = self.vdd_position.y - inverter_well_ypos \ - + self.nwell_enclose_active + drc["minwidth_tx"] - - # FIXME fudge factor xpos - offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos] - self.add_rect(layer="nwell", - offset=offset, - width=well_width, - height=well_height) + # FIXME fudge factor xpos + offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos] + self.add_rect(layer="nwell", + offset=offset, + width=well_width, + height=well_height) # add well contacts # connect pimplants to gnd diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6d6c6ce5..1e68fc2e 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -45,7 +45,7 @@ class lib: """ Determine the load/slews if they aren't specified in the config file. """ # These are the parameters to determine the table sizes #self.load_scales = np.array([0.1, 0.25, 0.5, 1, 2, 4, 8]) - self.load_scales = np.array([0.25, 1, 8]) + self.load_scales = np.array([0.25, 1, 4]) #self.load_scales = np.array([0.25, 1]) self.load = tech.spice["dff_in_cap"] self.loads = self.load_scales*self.load @@ -621,17 +621,12 @@ class lib: )) # information of checks - from hierarchy_design import total_drc_errors - from hierarchy_design import total_lvs_errors - DRC = 'skipped' - LVS = 'skipped' - if OPTS.check_lvsdrc: - DRC = str(total_drc_errors) - LVS = str(total_lvs_errors) - - datasheet.write("{0},{1},".format(DRC, LVS)) + (drc_errors, lvs_errors) = self.sram.DRC_LVS(final_verification=True) + datasheet.write("{0},{1},".format(drc_errors, lvs_errors)) + # write area - datasheet.write(str(self.sram.width * self.sram.height)+',') + datasheet.write(str(self.sram.width * self.sram.height) + ',') + # write timing information for all ports for port in self.all_ports: #din timings diff --git a/compiler/modules/dff_buf.py b/compiler/modules/dff_buf.py index 6f06b778..e42c01c0 100644 --- a/compiler/modules/dff_buf.py +++ b/compiler/modules/dff_buf.py @@ -91,8 +91,8 @@ class dff_buf(design.design): def create_instances(self): self.dff_inst=self.add_inst(name="dff_buf_dff", mod=self.dff) - self.connect_inst(props.dff_buff.buf_ports) - #self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) + + self.connect_inst(["D", "qint", "clk", "vdd", "gnd"]) self.inv1_inst=self.add_inst(name="dff_buf_inv1", mod=self.inv1) diff --git a/compiler/modules/hierarchical_decoder.py b/compiler/modules/hierarchical_decoder.py index 6e98c5e0..0a54a401 100644 --- a/compiler/modules/hierarchical_decoder.py +++ b/compiler/modules/hierarchical_decoder.py @@ -25,8 +25,7 @@ class hierarchical_decoder(design.design): self.pre2x4_inst = [] self.pre3x8_inst = [] - b = factory.create(module_type="bitcell") - self.cell_height = b.height + (self.cell_height, self.cell_multiple) = self.find_decoder_height() self.rows = rows self.num_inputs = math.ceil(math.log(self.rows, 2)) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) @@ -35,6 +34,24 @@ class hierarchical_decoder(design.design): if not OPTS.netlist_only: self.create_layout() + def find_decoder_height(self): + b = factory.create(module_type="bitcell") + # Old behavior + return (b.height, 1) + + # Search for the smallest multiple that works + cell_multiple = 1 + while cell_multiple < 3: + cell_height = cell_multiple * b.height + and3 = factory.create(module_type="pand3", + height=cell_height) + (drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) + if drc_errors + lvs_errors == 0: + return (cell_height, cell_multiple) + cell_multiple += 1 + else: + debug.error("Couldn't find a valid decoder height multiple.", -1) + def create_netlist(self): self.add_modules() self.setup_netlist_constants() diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index d5b0e799..af612af4 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -73,11 +73,12 @@ class precharge_array(design.design): def add_layout_pins(self): + en_bar_pin = self.pc_cell.get_pin("en_bar") self.add_layout_pin(text="en_bar", - layer="m1", - offset=self.pc_cell.get_pin("en_bar").ll(), + layer=en_bar_pin.layer, + offset=en_bar_pin.ll(), width=self.width, - height=drc("minwidth_m1")) + height=en_bar_pin.height()) for inst in self.local_insts: self.copy_layout_pin(inst, "vdd") diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 9aa54733..cc6fd0f8 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -215,7 +215,7 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputC_yoffset = self.inputB_yoffset - 1.1 * m1_pitch + self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, @@ -223,7 +223,7 @@ class pnand3(pgate.pgate): position="center") # FIXME: constant hack - self.inputA_yoffset = self.inputB_yoffset + 1.1 * m1_pitch + self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index ee0f96d1..afe14a05 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -111,7 +111,7 @@ class precharge(design.design): vertical=True) # Hack for li layers - if OPTS.tech_name == "s8": + if hasattr(self, "li_stack"): self.add_via_center(layers=self.li_stack, offset=self.well_contact_pos) @@ -166,7 +166,7 @@ class precharge(design.design): Connects the upper and lower pmos together """ - offset = self.lower_pmos_inst.get_pin("G").ll() + offset = self.lower_pmos_inst.get_pin("G").ul() # connects the top and bottom pmos' gates together ylength = self.upper_pmos1_inst.get_pin("G").ll().y - offset.y self.add_rect(layer="poly", @@ -198,6 +198,9 @@ class precharge(design.design): if self.en_layer == "m2": self.add_via_center(layers=self.m1_stack, offset=offset) + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=offset) # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -220,6 +223,9 @@ class precharge(design.design): offset=self.well_contact_pos, implant_type="n", well_type="n") + if hasattr(self, "li_stack"): + self.add_via_center(layers=self.li_stack, + offset=self.well_contact_pos) self.height = self.well_contact_pos.y + contact.active_contact.height + self.m1_space diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index a305f620..357e1ce4 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -12,6 +12,8 @@ from vector import vector from sram_factory import factory import contact import logical_effort +import os +from globals import OPTS class ptx(design.design): @@ -101,21 +103,38 @@ class ptx(design.design): dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] self.add_pin_list(pin_list, dir_list) - # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, - # " ".join(self.pins))) # Just make a guess since these will actually # be decided in the layout later. area_sd = 2.5 * self.poly_width * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width - main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], - self.mults, - self.tx_width, - drc("minwidth_poly")) - area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, - area_sd) + if OPTS.tech_name == "s8": + print("here {0}".format(self.name)) + # s8 technology is in microns + main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + # Perimeters are in microns + # Area is in u since it is microns square + area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd, + area_sd) + else: + main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, + area_sd) self.spice_device = main_str + area_str self.spice.append("\n* ptx " + self.spice_device) - # self.spice.append(".ENDS {0}".format(self.name)) + + # LVS lib is always in SI units + if os.path.exists(OPTS.openram_tech + "lvs_lib"): + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.mults, + self.tx_width, + drc("minwidth_poly")) + def setup_layout_constants(self): """ @@ -197,6 +216,8 @@ class ptx(design.design): # The well is not included in the height and width self.height = self.poly_height self.width = self.active_width + self.well_height = self.height + self.well_width = self.width # This is the center of the first active contact offset (centered vertically) self.contact_offset = self.active_offset + vector(0.5 * self.active_contact.width, @@ -354,10 +375,10 @@ class ptx(design.design): if not (well_name in layer or "vtg" in layer): return - center_pos = self.active_offset + vector(0.5*self.active_width, - 0.5*self.active_height) - well_ll = center_pos - vector(0.5*self.well_width, - 0.5*self.well_height) + center_pos = self.active_offset + vector(0.5 * self.active_width, + 0.5 * self.active_height) + well_ll = center_pos - vector(0.5 * self.well_width, + 0.5 * self.well_height) if well_name in layer: self.add_rect(layer=well_name, offset=well_ll, @@ -471,7 +492,7 @@ class ptx(design.design): contact=self.add_via_center(layers=self.active_stack, offset=pos, size=(1, self.num_contacts), - directions=("V","V"), + directions=("V", "V"), implant_type=self.implant_type, well_type=self.well_type) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 0efad5cc..8863c299 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -51,6 +51,9 @@ class sram(): def sp_write(self, name): self.s.sp_write(name) + def lvs_write(self, name): + self.s.lvs_write(name) + def lef_write(self, name): self.s.lef_write(name) diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index e8a91486..b9c4c909 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -121,7 +121,7 @@ class sram_base(design, verilog, lef): start_time = datetime.datetime.now() # We only enable final verification if we have routed the design - self.DRC_LVS(final_verification=OPTS.route_supplies, top_level=True) + self.DRC_LVS(final_verification=OPTS.route_supplies) if not OPTS.is_unit_test: print_time("Verification", datetime.datetime.now(), start_time) @@ -561,7 +561,7 @@ class sram_base(design, verilog, lef): self.add_via_center(layers=self.m2_stack, offset=out_pos) - def sp_write(self, sp_name): + def sp_write(self, sp_name, lvs_netlist=False): # Write the entire spice of the object to the file ############################################################ # Spice circuit @@ -574,17 +574,20 @@ class sram_base(design, verilog, lef): sp.write("* Data bits: {}\n".format(self.word_size)) sp.write("* Banks: {}\n".format(self.num_banks)) sp.write("* Column mux: {}:1\n".format(self.words_per_row)) - sp.write("**************************************************\n") + sp.write("**************************************************\n") # This causes unit test mismatch # sp.write("* Created: {0}\n".format(datetime.datetime.now())) # sp.write("* User: {0}\n".format(getpass.getuser())) - # sp.write(".global {0} {1}\n".format(spice["vdd_name"], + # sp.write(".global {0} {1}\n".format(spice["vdd_name"], # spice["gnd_name"])) usedMODS = list() - self.sp_write_file(sp, usedMODS) + self.sp_write_file(sp, usedMODS, lvs_netlist=lvs_netlist) del usedMODS sp.close() - + + def lvs_write(self, sp_name): + self.sp_write(sp_name, lvs_netlist=True) + def get_wordline_stage_efforts(self, inp_is_rise=True): """Get the all the stage efforts for each stage in the path from clk_buf to a wordline""" stage_effort_list = [] diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index 35ac5d76..0acc8926 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -45,7 +45,9 @@ class library_lvs_test(openram_test): def setup_files(): gds_dir = OPTS.openram_tech + "/gds_lib" - sp_dir = OPTS.openram_tech + "/sp_lib" + sp_dir = OPTS.openram_tech + "/lvs_lib" + if not os.path.exists(sp_dir): + sp_dir = OPTS.openram_tech + "/sp_lib" files = os.listdir(gds_dir) nametest = re.compile("\.gds$", re.IGNORECASE) gds_files = list(filter(nametest.search, files)) diff --git a/compiler/tests/04_precharge_pbitcell_test.py b/compiler/tests/04_precharge_pbitcell_test.py new file mode 100755 index 00000000..fd2f8283 --- /dev/null +++ b/compiler/tests/04_precharge_pbitcell_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check precharge in multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + factory.reset() + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking precharge for pbitcell (innermost connections)") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") + self.local_check(tx) + + factory.reset() + debug.info(2, "Checking precharge for pbitcell (outermost connections)") + tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") + self.local_check(tx) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/04_precharge_test.py b/compiler/tests/04_precharge_test.py index 1c12ad8b..76d433a8 100755 --- a/compiler/tests/04_precharge_test.py +++ b/compiler/tests/04_precharge_test.py @@ -15,6 +15,7 @@ from globals import OPTS from sram_factory import factory import debug + class precharge_test(openram_test): def runTest(self): @@ -26,27 +27,6 @@ class precharge_test(openram_test): tx = factory.create(module_type="precharge", size=1) self.local_check(tx) - # check precharge in multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 1 - - factory.reset() - debug.info(2, "Checking precharge for pbitcell (innermost connections)") - tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(tx) - - factory.reset() - debug.info(2, "Checking precharge for pbitcell (innermost connections)") - tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl1", bitcell_br="br1") - self.local_check(tx) - - factory.reset() - debug.info(2, "Checking precharge for pbitcell (outermost connections)") - tx = factory.create(module_type="precharge", size=1, bitcell_bl="bl2", bitcell_br="br2") - self.local_check(tx) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/06_hierarchical_decoder_pbitcell_test.py b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py new file mode 100755 index 00000000..1fb3a3f6 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_pbitcell_test.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class hierarchical_decoder_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + # check hierarchical decoder for multi-port + OPTS.bitcell = "pbitcell" + OPTS.num_rw_ports = 1 + OPTS.num_w_ports = 0 + OPTS.num_r_ports = 0 + + factory.reset() + debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=16) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=17) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=23) + self.local_check(a) + + debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=32) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=65) + self.local_check(a) + + debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=128) + self.local_check(a) + + factory.reset() + debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=341) + self.local_check(a) + + debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") + a = factory.create(module_type="hierarchical_decoder", rows=512) + self.local_check(a) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py index 9d8353f3..ab7e844f 100755 --- a/compiler/tests/06_hierarchical_decoder_test.py +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -63,49 +63,6 @@ class hierarchical_decoder_test(openram_test): a = factory.create(module_type="hierarchical_decoder", rows=512) self.local_check(a) - # check hierarchical decoder for multi-port - OPTS.bitcell = "pbitcell" - OPTS.num_rw_ports = 1 - OPTS.num_w_ports = 0 - OPTS.num_r_ports = 0 - - factory.reset() - debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=16) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=17) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=23) - self.local_check(a) - - debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=32) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=65) - self.local_check(a) - - debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=128) - self.local_check(a) - - factory.reset() - debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=341) - self.local_check(a) - - debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") - a = factory.create(module_type="hierarchical_decoder", rows=512) - self.local_check(a) - globals.end_openram() # run the test from the command line diff --git a/compiler/tests/08_precharge_array_1rw1r_test.py b/compiler/tests/08_precharge_array_1rw1r_test.py new file mode 100755 index 00000000..8f51c2d4 --- /dev/null +++ b/compiler/tests/08_precharge_array_1rw1r_test.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys,os +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + +class precharge_test(openram_test): + + def runTest(self): + config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + # check precharge array in multi-port + OPTS.bitcell = "bitcell_1rw_1r" + OPTS.num_rw_ports = 1 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 0 + + factory.reset() + debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") + pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") + self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") + # pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0") + # self.local_check(pc) + + # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") + # pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2") + # self.local_check(pc) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py index d4f5591b..b301a909 100755 --- a/compiler/tests/08_precharge_array_test.py +++ b/compiler/tests/08_precharge_array_test.py @@ -25,25 +25,6 @@ class precharge_test(openram_test): debug.info(2, "Checking 3 column precharge") pc = factory.create(module_type="precharge_array", columns=3) self.local_check(pc) - - # check precharge array in multi-port - OPTS.bitcell = "bitcell_1rw_1r" - OPTS.num_rw_ports = 1 - OPTS.num_r_ports = 1 - OPTS.num_w_ports = 0 - - factory.reset() - debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell") - pc = factory.create(module_type="precharge_array", columns=3, bitcell_bl="bl0", bitcell_br="br0") - self.local_check(pc) - - # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") - # pc = precharge_array.precharge_array(name="pre3", columns=3, bitcell_bl="bl0", bitcell_br="br0") - # self.local_check(pc) - - # debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") - # pc = precharge_array.precharge_array(name="pre4", columns=3, bitcell_bl="bl2", bitcell_br="br2") - # self.local_check(pc) globals.end_openram() diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py index c385e455..120974ff 100755 --- a/compiler/tests/25_verilog_sram_test.py +++ b/compiler/tests/25_verilog_sram_test.py @@ -19,7 +19,9 @@ class verilog_test(openram_test): def runTest(self): config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) globals.init_openram(config_file) - + OPTS.route_supplies=False + OPTS.check_lvsdrc=False + OPTS.netlist_only=True from sram import sram from sram_config import sram_config c = sram_config(word_size=2, diff --git a/compiler/tests/testutils.py b/compiler/tests/testutils.py index 95675c38..c4fd7cac 100644 --- a/compiler/tests/testutils.py +++ b/compiler/tests/testutils.py @@ -39,7 +39,7 @@ class openram_test(unittest.TestCase): tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name) tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name) - a.sp_write(tempspice) + a.lvs_write(tempspice) # cannot write gds in netlist_only mode if not OPTS.netlist_only: a.gds_write(tempgds) diff --git a/compiler/verify/none.py b/compiler/verify/none.py index 9d7ac938..41e5780e 100644 --- a/compiler/verify/none.py +++ b/compiler/verify/none.py @@ -16,14 +16,16 @@ drc_warned = False lvs_warned = False pex_warned = False + def run_drc(cell_name, gds_name, extract=False, final_verification=False): global drc_warned if not drc_warned: debug.warning("DRC unable to run.") drc_warned=True - # Since we warned, return a failing test. + # Since we warned, return a failing test. return 1 - + + def run_lvs(cell_name, gds_name, sp_name, final_verification=False): global lvs_warned if not lvs_warned: @@ -32,17 +34,23 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): # Since we warned, return a failing test. return 1 + def run_pex(name, gds_name, sp_name, output=None, final_verification=False): - global pex_warned + global pex_warned if not pex_warned: debug.warning("PEX unable to run.") pex_warned=True - # Since we warned, return a failing test. + # Since we warned, return a failing test. return 1 + def print_drc_stats(): pass + + def print_lvs_stats(): pass + + def print_pex_stats(): pass