import debug import re import os class spice: """ This provides a set of useful generic types for hierarchy management. If a module is a custom designed cell, it will read from the GDS and spice files and perform LVS/DRC. If it is dynamically generated, it should implement a constructor to create the layout/netlist and perform LVS/DRC. Class consisting of a set of modules and instances of these modules """ def __init__(self, name): self.name = name self.mods = [] # Holds subckts/mods for this module self.pins = [] # Holds the pins for this module # for each instance, this is the set of nets/nodes that map to the pins for this instance # THIS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the # Spice format) self.conns = [] self.sp_read() ############################################################ # Spice circuit ############################################################ def add_pin(self, name): """Adds a pin to the pins list""" self.pins.append(name) def add_pin_list(self, pin_list): """Adds a pin_list to the pins list""" self.pins = self.pins + pin_list def add_mod(self, mod): """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 other function with the check to find if there is a problem. The check otion can be set to false where we dynamically generate groups of connections after a group of modules are generated.""" if (check and (len(self.insts[-1].mod.pins) != len(args))): debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins), len(args)), 1) self.conns.append(args) if check and (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("-----") debug.error("Connections: \n"+str(self.conns),1) def sp_read(self): """Reads the sp file (and parse the pins) from the library Otherwise, initialize it to null for dynamic generation""" if os.path.isfile(self.sp_file): debug.info(2, "opening {0}".format(self.sp_file)) f = open(self.sp_file) self.spice = f.readlines() for i in range(len(self.spice)): self.spice[i] = self.spice[i].rstrip(" \n") # find first subckt line in the file subckt = re.compile("^.subckt", re.IGNORECASE) subckt_line = filter(subckt.search, self.spice)[0] # parses line into ports and remove subckt self.pins = subckt_line.split(" ")[2:] else: self.spice = [] def contains(self, mod, modlist): for x in modlist: if x.name == mod.name: 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""" 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) 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))) # 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.name, len(self.insts), len(self.conns))) debug.error("Instances: \n"+str(self.insts)) debug.error("-----") 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 sp.write("X{0} {1} {2}\n".format(self.insts[i].name, " ".join(self.conns[i]), self.insts[i].mod.name)) sp.write(".ENDS {0}\n".format(self.name)) else: # write the subcircuit itself # Including the file path makes the unit test fail for other users. #if os.path.isfile(self.sp_file): # sp.write("\n* {0}\n".format(self.sp_file)) sp.write("\n".join(self.spice)) sp.write("\n") def sp_write(self, spname): """Writes the spice 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) del usedMODS spfile.close()