def __init__(self, fl, line_num, uid, props, params=None): self._fl = fl self._line_num = line_num self._uid = uid self._lazy_statements = {} self._amb_types = {} self._inline_comment = None self._st_language = None if isinstance(props, str): self._props = OrderedDict() self._props[Types.name] = props elif isinstance(props, dict): self._props = OrderedDict(props) elif props is None: self._props = OrderedDict() else: raise InvalidTypeException('props must be a dict or str') if isinstance(params, str): self._params = OrderedDict() self._params[Types.name] = params elif isinstance(params, dict): self._params = OrderedDict(params) elif params is None: self._params = OrderedDict() else: raise InvalidTypeException('params must be a dict or str')
def add_device(self, st, case_insensitive=False): """ Adds a statement to the scope tree. If LAZY_STATEMENT was added: Takes in a statement and finds its LAZY_OBJECT equivilent, and updates the scope tree (remove LAZY_OBJECT and add to current scope). It also calls bind() on the LAZY_OBJECT so all referencing Statements can be updated to reference to the correct object. Args: st (Statement): Statement to add to scoping case_insensitive Throws: NameConflictException if the name conflicts with another name within the scope """ if isinstance(st, Device): st.set_prop(Types.statementType, "__DEVICE__") self._add_statement(st, True, case_insensitive) self._name_to_statement[st.get_prop(Types.name)] = st else: raise InvalidTypeException(st.name + " is not of type Device")
def add(self, st, case_insensitive=False): """ Generic add function. This takes in ModelDef, Device, ENODE, and str. If the object is a str, then it will assume this is a lazy object (type is not yet known). Args: st (ModelDef, Device, ENODE, or str): Object to add to index case_insensitive Throws: InvalidTypeException. Raised if st is of an unknown type """ if isinstance(st, ModelDef): self.add_model(st, case_insensitive=case_insensitive) elif isinstance(st, Device): self.add_device(st, case_insensitive=case_insensitive) elif isinstance(st, ENODE): self.add_enode(st, case_insensitive=case_insensitive) elif isinstance(st, Ref): self.add_ref(st) elif isinstance(st, LAZY_STATEMENT): self.add_lazy_statement(st) elif isinstance(st, Command): st.set_prop(Types.statementType, "__COMMAND__") self._add_statement(st) # elif isinstance(st, SUBCKT): # st.set_prop(Types.statementType, "__SUBCKT__") # self._add_statement(st, case_insensitive=case_insensitive) else: raise InvalidTypeException( st + ' must be of type ModelDef, Device, ENODE, or str')
def add(self, ws): """ Adds a Statement to the index Args: ws (Statement): Statement to be indexed """ if not isinstance(ws, self._t): raise InvalidTypeException(ws.__class__.__name__ + " is not instance of " + self._t.__name__) # get the files name nm = self._f(ws) # if first entry, then create array if nm not in self._file_dict: self._file_dict[nm] = [] self._file_dict_keys[nm] = [] if self._s is not None: # get the line number key = self._s(ws) # find the position in the list that the statement # belongs in, then insert both the key and the statement # at that position key_pos = bisect_left(self._file_dict_keys[nm], key) self._file_dict_keys[nm].insert(key_pos, key) self._file_dict[nm].insert(key_pos, ws) else: self._file_dict[nm].append(ws)
def add_device_type(self, device_type): if isinstance(device_type, XmlDeviceType): if not self._device_types.get(device_type.name): self._device_types[device_type.name] = [] self._device_types[device_type.name].append(device_type) else: raise InvalidTypeException(device_type + " is not of type DeviceType")
def add_ref(self, st): """ Adds a Ref to the scope tree. Args: st (Statement): Statement to add to scoping """ if isinstance(st, Ref): st.set_prop(Types.statementType, "__REF__") self._add_statement(st) else: raise InvalidTypeException(st.name + " is not of type Ref")
def add_index(self, idx): """ Adds an index to the data model. Args: idx (StatementIndex): Statement index to add Throws: InvalidTypeException. If the user passes in idx that is of an incorrect type """ if isinstance(idx, StatementIndex): self._indexes.append((idx.type, idx)) else: raise InvalidTypeException(idx + " is not of type StatementIndex")
def add_enode(self, e, case_insensitive=False): """ Adds a ENODE to the scope tree. ENODES will not bind to LAZY_STATEMENTs and attempts to add an ENODE that conflicts with a LAZY_STATEMENT will get a NameConflictException Args: e (Statement): Statement to add to scoping case_insensitive Throws: NameConflictException if the name conflicts with another name within the scope """ if isinstance(e, ENODE): e.set_prop(Types.statementType, "__ENODE__") self._add_statement(e, case_insensitive) else: raise InvalidTypeException(e.name + " is not of type ENODE")
def set_lazy_statement(self, o, types): """ Sets a lazy object to a device. This is used when o has not been properly defined when the current line is parsed. The parser should know a list of possibilities and this is passed in as well. When the object is defined, it will call bind causing this object to properly set references to o. Args: o (LAZY_STATEMENT): Object who has not yet been defined types (list<__class__>): List of all possible classes that o could be. Throws: InvalidTypeException. if o is not of type LAZY_STATEMENT """ if 'add_listener' not in o.__class__.__dict__: raise InvalidTypeException( str(o) + " (" + str(o.__class__) + ") is not of type LAZY_STATEMENT") self._lazy_statements[o.name] = types o.add_listener(self)
def __init__(self, trans_type): if trans_type.upper() not in transient_value_map.keys(): raise InvalidTypeException(trans_type + " is not a valid type of transient") self._transType = trans_type.upper() self._transParams = OrderedDict()
def add_directive_type(self, directive_type): if isinstance(directive_type, XmlDirectiveType): self._directive_types[directive_type.identity()] = directive_type else: raise InvalidTypeException(directive_type + " is not of type DirectiveType")
def add_model(self, m, case_insensitive=False): """ Adds a model to the scope. It takes in a ModelDef and either adds it to an existing MASTER_MODEL, or creates a new MASTER_MODEL and adds it to the scope. All ModelDefs and MASTER_MODEL must be defined within the same scope. The ModefDef is also passed to existing indexes to be properly indexed. The MASTER_MODEL is passed to existing indexes when it is created. Args: m (ModelDef): Model added to scope case_insensitive Returns: MASTER_MODEL: parent of ModelDef Throws: NameConflictException if there is another object with the same name as m that is not a corresponding MASTER_MODEL or MASTER_MODEL within the same scope. InvalidModelException if m is not a ModelDef """ master = None # type checking if not isinstance(m, ModelDef): raise InvalidTypeException(m.name + " is not of type ModeDef") # If MASTER_MODEL already exists model_name = m.name if case_insensitive: model_name = m.name.upper() if ("__MODELDEF__" + model_name) in self._statements and isinstance( self._statements[("__MODELDEF__" + model_name)], MASTER_MODEL): master = self._statements[("__MODELDEF__" + model_name)] else: d = self.get_object("__LAZYSTATEMENT__" + model_name) master = MASTER_MODEL(m.name) if isinstance(d, LAZY_STATEMENT): d.bind(master, case_insensitive) self.remove_statement(d) if self._lib_command is None: if self.scope_contains("__MODELDEF__" + model_name): raise NameConflictException( model_name + " has already been used in this scope") else: if self.local_scope_contains("__MODELDEF__" + model_name): raise NameConflictException( model_name + " has already been used in this scope") self._statements["__MODELDEF__" + model_name] = master # Put MASTER_MODEL in indexes (if any care to see it) self._add_to_indexes(master) m.set_prop(Types.statementType, "__MODELDEF__") master.add_model(m) # Add ModelDef to indexes self._add_to_indexes(m) return master
def read_line(self, parsed_netlist_line, reader_state, top_reader_state, language_definition, control_device_handling_list, inc_files_and_scopes, lib_files): """ Reads a netlist line and calls the appropriate XDMFactory method to insert the statement into the data model. """ if parsed_netlist_line.flag_top_pnl: top_reader_state.add_unknown_pnl(parsed_netlist_line) return parsed_netlist_line.linenum[-1] if XDMFactory.is_supported_device(parsed_netlist_line): device = XDMFactory.build_device(parsed_netlist_line, reader_state, language_definition) if device is None: return parsed_netlist_line.linenum[-1] # handle case of preprocess directive for xyce if parsed_netlist_line.preprocess_keyword_value and "hspice" in language_definition.language: # create parsed netlist line object for the preprocess directive preprocess_pnl = ParsedNetlistLine(parsed_netlist_line.filename, [0]) preprocess_pnl.type = ".PREPROCESS" preprocess_pnl.local_type = ".PREPROCESS" preprocess_pnl.add_known_object(parsed_netlist_line.preprocess_keyword_value[0].split()[0], "PREPROCESS_KEYWORD_VALUE") preprocess_pnl.add_value_to_value_list(parsed_netlist_line.preprocess_keyword_value[0].split()[1]) # check if preprocess directive already in index preprocess_uid = -1 for fl, objs in reader_state.scope_index.source_line_index: for obj in objs: last_uid = obj.uid if isinstance(obj, Command): if obj.command_type == ".PREPROCESS": preprocess_uid = obj.uid # if preprocess directive not in index, add in as second index after TITLE object if preprocess_uid < 0: XDMFactory.build_directive(preprocess_pnl, reader_state, language_definition, self._lib_sect_list) if device.resolve_control_devices: control_device_handling_list.append((device, reader_state.scope_index)) if device.device_type == "X": reader_state.add_subcircuit_device(device, reader_state.scope_index) elif XDMFactory.is_unknown_device(parsed_netlist_line): reader_state.add_unknown_pnl(parsed_netlist_line) elif XDMFactory.is_supported_directive(parsed_netlist_line): # for case of standalone .PARAM statements with no actual parameters! if parsed_netlist_line.type == ".PARAM" and not parsed_netlist_line.params_dict: parsed_netlist_line.type = "COMMENT" parsed_netlist_line.local_type = "" parsed_netlist_line.name = ".PARAM" parsed_netlist_line.params_dict["COMMENT"] = ".PARAM" XDMFactory.build_comment(parsed_netlist_line, reader_state) # BEWARE: hack for Spectre -- takes .PARAM params from inside subckt and moves them to paramsList for subckt # UPDATE: 2019-06-20 -- not sure if the hack is actually needed for Spectre translation, but it seems to # cause problems with HSPICE translation (possibly PSPICE as well). But to err on the # side of caution, will keep code but will check input language being Spectre # before moving into the hacked code block elif parsed_netlist_line.type == ".PARAM" and not reader_state.scope_index.is_top_parent() and language_definition._language.upper() == "spectre": reader_state.scope_index.subckt_command.set_prop(Types.subcircuitParamsList, parsed_netlist_line.params_dict) else: directive = XDMFactory.build_directive(parsed_netlist_line, reader_state, language_definition, self._lib_sect_list) if parsed_netlist_line.type == ".END": reader_state.end_directive = directive elif parsed_netlist_line.type == ".MODEL": XDMFactory.build_model(parsed_netlist_line, reader_state, language_definition) elif parsed_netlist_line.type == ".INC" or parsed_netlist_line.type == ".INCLUDE": if not parsed_netlist_line.known_objects[Types.fileNameValue] in self._reader_state.master_inc_list: self._reader_state.add_master_inc_list(parsed_netlist_line.known_objects[Types.fileNameValue]) self._reader_state.add_master_inc_list_scopes(reader_state.scope_index) inc_files_and_scopes.append((parsed_netlist_line.known_objects[Types.fileNameValue], reader_state.scope_index)) elif parsed_netlist_line.known_objects[Types.fileNameValue] in self._reader_state.master_inc_list and reader_state.scope_index.is_top_parent(): for ind, file_and_scope in enumerate(inc_files_and_scopes): (filename, scope) = file_and_scope if parsed_netlist_line.known_objects[Types.fileNameValue] == filename: inc_files_and_scopes[ind] = (parsed_netlist_line.known_objects[Types.fileNameValue], reader_state.scope_index) inc_ind = self._reader_state.master_inc_list.index(parsed_netlist_line.known_objects[Types.fileNameValue]) self._reader_state.master_inc_list_scopes[inc_ind] = reader_state.scope_index # For filenames enclosed in single quotes, ntpath doesn't seem to strip trailing # single quote. Therefore, will strip the single quotes before before passing # to ntpath. parsed_netlist_line.known_objects[Types.fileNameValue] = \ ntpath.split(parsed_netlist_line.known_objects[Types.fileNameValue].replace("'", "").replace("\"", ""))[1] XDMFactory.build_directive(parsed_netlist_line, reader_state, language_definition, self._lib_sect_list) elif parsed_netlist_line.type == ".LIB": # Prepare lib_file name if parsed_netlist_line.known_objects.get(Types.fileNameValue): lib_file = parsed_netlist_line.known_objects[Types.fileNameValue].replace("'", '').replace('"', '') if not os.path.isfile(lib_file): lib_file = os.path.join(os.path.dirname(self._file), lib_file) # Only parse .LIB statements if they are on the parent scope (the scope that # includes the stuff that actually needs to be simulated). Other .LIB sections # are unused sections that aren't called by current simulation. This avoids # multiple parsing/writing of same files. if parsed_netlist_line.known_objects.get(Types.fileNameValue) and reader_state.scope_index.is_top_parent(): # if .lib command calls a section within the same file, add it to list of sections to parse # for the current file. otherwise, add file/section to list of libraries to be parsed later if os.path.normpath(self._file) == os.path.normpath(lib_file): self._lib_sect_list.append(parsed_netlist_line.known_objects[Types.libEntry]) child = self._reader_state.scope_index.get_child_scope(parsed_netlist_line.known_objects[Types.libEntry]) if not child is None: reader_state.scope_index.retroactive_add_statement(child) elif parsed_netlist_line.known_objects.get(Types.libEntry): lib_files.append((parsed_netlist_line.known_objects[Types.fileNameValue], parsed_netlist_line.known_objects[Types.libEntry])) else: lib_files.append((parsed_netlist_line.known_objects[Types.fileNameValue], parsed_netlist_line.known_objects[Types.fileNameValue])) if not lib_file in self._reader_state.lib_files_in_scope: self._reader_state.add_lib_files_in_scope(lib_file) if lib_file in self._reader_state.lib_files_not_in_scope: self._reader_state.remove_lib_files_not_in_scope(lib_file) elif parsed_netlist_line.known_objects.get(Types.fileNameValue) and not reader_state.scope_index.is_top_parent(): # for the case of a .lib file that isn't used by the top scope, and in included # by a scope outside of the top scope, the file will need to be translated. # unique files (file cannot be saved more than once) are saved to a tracking list # (self._lib_files_not_in_scope) to be parsed at the very end. # in case a library section may be added in to top scope by a .lib statement later in the file. save # other .lib sects included, for retroactive processing if os.path.normpath(self._file) == os.path.normpath(lib_file): self._reader_state.scope_index.add_child_scope_lib_sects(parsed_netlist_line.known_objects[Types.libEntry]) # for libraries added in child scope in different files elif os.path.normpath(self._file) != os.path.normpath(lib_file): if not lib_file in self._reader_state.lib_files_not_in_scope and not lib_file in self._reader_state.lib_files_in_scope: # only file name needs to be saved - the whole file is outside the top scope, # so the library section doesn't matter self._reader_state.add_lib_files_not_in_scope(lib_file) if parsed_netlist_line.known_objects.get(Types.fileNameValue): parsed_netlist_line.known_objects[Types.fileNameValue] = \ ntpath.split(parsed_netlist_line.known_objects[Types.fileNameValue].replace("'", "").replace("\"", ""))[1] XDMFactory.build_directive(parsed_netlist_line, reader_state, language_definition, self._lib_sect_list) elif parsed_netlist_line.type == "DATA": XDMFactory.build_data(parsed_netlist_line, reader_state) elif parsed_netlist_line.type == "TITLE": XDMFactory.build_title(parsed_netlist_line, reader_state) elif parsed_netlist_line.type == "COMMENT": XDMFactory.build_comment(parsed_netlist_line, reader_state) # spectre simulator command. defines language type elif parsed_netlist_line.type == "simulator": lang_type = parsed_netlist_line.params_dict.get('lang') # for x in lang_type : # # print (x) # # for y in lang_type[x]: # # print (y, ":", lang_type[x][y]) # print (lang_type['lang']) if 'spice' in lang_type: logging.info("Spectre Simulator Command Found. Switching parse mode to spice.") xml_factory = XmlFactory(self._hspice_xml) xml_factory.read() self._language_definition = xml_factory.language_definition self._grammar_type = HSPICENetlistBoostParserInterface self._language_changed = True elif 'spectre' in lang_type: logging.info("Spectre Simulator Command Found. Switching parse mode to spectre.") xml_factory = XmlFactory(self._spectre_xml) xml_factory.read() self._language_definition = xml_factory.language_definition self._grammar_type = SpectreNetlistBoostParserInterface self._language_changed = True else: logging.error("Unable to parse line: " + str(parsed_netlist_line.linenum)) raise InvalidTypeException() return parsed_netlist_line.linenum[-1]