-
Notifications
You must be signed in to change notification settings - Fork 78
Add SPICE tools #439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jwright6323
wants to merge
10
commits into
master
Choose a base branch
from
uniqsp
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add SPICE tools #439
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
e75d524
WIP
jwright6323 d15a3e1
passes type checker
jwright6323 e6eddba
Add tests, pass
jwright6323 f1478a6
Update src/hammer-vlsi/hammer_utils/spice_utils.py
jwright6323 8886802
Review feedback
jwright6323 980e44b
Merge branch 'master' of github.com:ucb-bar/hammer into uniqsp
jwright6323 73bce8e
missed some types
jwright6323 c16247b
Merge branch 'uniqsp' of github.com:ucb-bar/hammer into uniqsp
jwright6323 6583c52
Eventually I'll get better at this.
jwright6323 b93d3c1
Merge branch 'master' of github.com:ucb-bar/hammer into uniqsp
jwright6323 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,140 @@ | ||
| #!/usr/bin/env python3 | ||
| # -*- coding: utf-8 -*- | ||
| # | ||
| # spice_utils.py | ||
| # Misc SPICE utilities | ||
| # | ||
| # See LICENSE for licence details. | ||
|
|
||
| import re | ||
| from typing import Tuple, Optional, List, Set, Dict | ||
| from enum import Enum | ||
|
|
||
| class SpiceUtils: | ||
|
|
||
| # Multilines in spice start with a + character | ||
| MultilinePattern = re.compile("(?:\s*\n\+)+\s*", flags=re.DOTALL) | ||
| ModuleDefPattern = re.compile("^(\.subckt\s+)(?P<dname>[^\s]+)(\s+.*)$", flags=re.IGNORECASE|re.MULTILINE) | ||
| ModuleInstPattern = re.compile("^(x.*?\s)(?P<mname>[^\s]+)(\s*)$", flags=re.IGNORECASE|re.MULTILINE) | ||
| EndModulePattern = re.compile("^\.ends.*$", flags=re.IGNORECASE|re.MULTILINE) | ||
| Tokens = [ | ||
| ('SUBCKT', ModuleDefPattern.pattern), | ||
| ('ENDS', EndModulePattern.pattern), | ||
| ('INST', ModuleInstPattern.pattern) | ||
| ] | ||
| TokenRegex = re.compile('|'.join('(?P<%s>%s)' % t for t in Tokens), flags=re.IGNORECASE|re.MULTILINE) | ||
|
|
||
| @staticmethod | ||
| def uniquify_spice(sources: List[str]) -> List[str]: | ||
| """ | ||
| Uniquify the provided SPICE sources. If a module name exists in multiple files, each duplicate module will be | ||
| renamed. If the original name of a duplicated module is used in a file where it is not defined, an | ||
| exception is thrown as it is impossible to know which renamed module to use. For example: | ||
|
|
||
| - file1.sp defines B and A, which instantiates B | ||
| - file2.sp defines B and C, which instantiates B | ||
|
|
||
| B would be renamed to B_0 in file1.sp | ||
| B would be renamed to B_1 in file2.sp | ||
|
|
||
| However, if file3.sp defines D, which instantiates B, an exception would be raised because we don't know if we | ||
| should chose B_0 or B_1. | ||
|
|
||
| :param sources: A list of the contents of SPICE source files (not the filenames) | ||
| :return: A list of SPICE sources with unique modules | ||
| """ | ||
| sources_no_multiline = [SpiceUtils.remove_multilines(s) for s in sources] | ||
| module_trees = [SpiceUtils.parse_module_tree(s) for s in sources_no_multiline] | ||
| found_modules = set() # type: Set[str] | ||
| duplicates = set() # type: Set[str] | ||
| extmods = set() # type: Set[str] | ||
| for tree in module_trees: | ||
| extmods.update(set(item for sublist in tree.values() for item in sublist if item not in set(tree.keys()))) | ||
| for module in tree: | ||
| if module in found_modules: | ||
| duplicates.add(module) | ||
| found_modules.add(module) | ||
|
|
||
| invalid_extmods = extmods.intersection(duplicates) | ||
| if len(invalid_extmods) != 0: | ||
| raise ValueError("Unable to resolve master for duplicate SPICE module name: {}".format(",".join(invalid_extmods))) | ||
|
|
||
| def replace_source(source: str) -> str: | ||
| replacements = {} # type: Dict[str, str] | ||
| for old in duplicates: | ||
| i = 0 | ||
| new = old | ||
| while new in found_modules: | ||
| new = "{d}_{i}".format(d=old, i=i) | ||
| i = i + 1 | ||
| found_modules.add(new) | ||
| replacements[old] = new | ||
|
|
||
| return SpiceUtils.replace_modules(source, replacements) | ||
|
|
||
| return [replace_source(s) for s in sources_no_multiline] | ||
|
|
||
|
|
||
| @staticmethod | ||
| def parse_module_tree(s: str) -> Dict[str, Set[str]]: | ||
| """ | ||
| Parse a SPICE file and return a dictionary that contains all found modules pointing to lists of their submodules. | ||
| The SPICE file must not contain multiline statements. | ||
|
|
||
| :param s: A SPICE file without any multiline statements | ||
| :return: A dictionary whose keys are all found modules and whose values are the list of submodules | ||
| """ | ||
| in_module = False | ||
| module_name = "" | ||
| tree = {} # type: Dict[str, Set[str]] | ||
| for m in SpiceUtils.TokenRegex.finditer(s): | ||
| kind = m.lastgroup | ||
| if kind == 'SUBCKT': | ||
| module_name = m.group("dname") | ||
| in_module = True | ||
| if module_name in tree: | ||
| raise ValueError("Multiple SPICE subckt definitions for \"{}\" in the same file".format(module_name)) | ||
| tree[module_name] = set() | ||
| elif kind == 'ENDS': | ||
| in_module = False | ||
| elif kind == 'INST': | ||
| if not in_module: | ||
| raise ValueError("Malformed SPICE source while parsing: \"{}\"".format(m.group())) | ||
| tree[module_name].add(m.group("mname")) | ||
| else: | ||
| assert False, "Should not get here" | ||
|
|
||
| return tree | ||
|
|
||
| @staticmethod | ||
| def replace_modules(source: str, mapping: Dict[str, str]) -> str: | ||
| """ | ||
| Replace module names in a provided SPICE file by the provided mapping. | ||
| The SPICE file must not contain multiline statements. | ||
|
|
||
|
|
||
| :param source: The input SPICE source without any multiline statements | ||
| :param mapping: A dictionary of old module names mapped to new module names | ||
| :return: SPICE source with the module names replaced | ||
| """ | ||
| # Not giving m a type because its type is different in different python3 versions :( | ||
| def repl_fn(m) -> str: | ||
| if m.group(2) in mapping: | ||
| return m.group(1) + mapping[m.group(2)] + m.group(3) | ||
| else: | ||
| return m.group(0) | ||
|
|
||
| return SpiceUtils.ModuleInstPattern.sub(repl_fn, SpiceUtils.ModuleDefPattern.sub(repl_fn, source)) | ||
|
|
||
| @staticmethod | ||
| def remove_multilines(s: str) -> str: | ||
| """ | ||
| Remove all multiline statements from the given SPICE source. | ||
|
|
||
| :param s: The SPICE source | ||
| :return: SPICE source without multiline statements | ||
| """ | ||
| return SpiceUtils.MultilinePattern.sub(" ", s) | ||
|
|
||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.