Tim Edwards | 5cad4f8 | 2021-10-04 12:31:36 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # This script traverses SPICE model files (e.g. from SKY130) and |
| 4 | # extracts only the wanted model section, removes all comments and |
| 5 | # empty lines, and resolves all includes so that a flat model file |
| 6 | # results. This should speed up ngspice starts. |
| 7 | # |
| 8 | # (c) 2021 Harald Pretl, Johannes Kepler University Linz |
| 9 | |
| 10 | import sys,re,os |
| 11 | |
| 12 | def process_file(file_in_name, top_file): |
| 13 | global is_warning |
| 14 | try: |
| 15 | f_in = open(file_in_name, 'r') |
| 16 | except FileNotFoundError: |
| 17 | print('Warning! File ' + file_in_name + ' not found.') |
| 18 | is_warning = True |
| 19 | return; |
| 20 | |
| 21 | # process_file can be called recursively, so that nested include |
| 22 | # files can be traversed |
| 23 | |
| 24 | # write_active indicates whether we are in the right model section; in |
| 25 | # include files, it is always true |
| 26 | |
| 27 | if top_file == True: |
| 28 | write_active = False |
| 29 | else: |
| 30 | write_active = True |
| 31 | |
| 32 | for line in f_in: |
| 33 | line_trim = (line.lower()).strip() |
| 34 | |
| 35 | if top_file == True: |
| 36 | # we assume that .lib statements are only used in the main file |
| 37 | if '.lib' in line_trim: |
| 38 | if model_section in line_trim: |
| 39 | write_active = True |
| 40 | else: |
| 41 | write_active = False |
| 42 | |
| 43 | if '.endl' == line_trim: |
| 44 | write_active = False |
| 45 | f_out.write(line) |
| 46 | |
| 47 | if len(line_trim) > 0: # write no empty lines |
| 48 | if (line_trim[0] != '*'): # write no comments |
| 49 | if (write_active == True): |
| 50 | if '.include' in line_trim: |
| 51 | # need to save and restore working dir so that nested |
| 52 | # includes work |
| 53 | current_wd = os.getcwd() |
| 54 | newfile = re.findall(r'"(.*?)(?<!\\)"', line_trim) |
| 55 | print('Reading ',newfile[0]) |
| 56 | |
| 57 | # enter new working dir |
| 58 | new_wd = os.path.dirname(newfile[0]) |
| 59 | if len(new_wd) > 0: |
| 60 | try: |
| 61 | os.chdir(new_wd) |
| 62 | except OSError: |
| 63 | print('Warning: Could not enter directory ' + new_wd) |
| 64 | is_warning = True |
| 65 | |
| 66 | # traverse into new include file |
| 67 | new_file_name = os.path.basename(newfile[0]) |
| 68 | process_file(new_file_name, False) |
| 69 | |
| 70 | # restore old working dir after return |
| 71 | os.chdir(current_wd) |
| 72 | else: |
| 73 | f_out.write(line) |
| 74 | |
| 75 | f_in.close() |
| 76 | return; |
| 77 | |
| 78 | # main routine |
| 79 | |
| 80 | if len(sys.argv) == 3: |
| 81 | model_section = sys.argv[2] |
| 82 | else: |
| 83 | model_section = 'tt' |
| 84 | |
| 85 | if (len(sys.argv) == 2) or (len(sys.argv) == 3): |
| 86 | infile_name = sys.argv[1] |
| 87 | outfile_name = infile_name + '.' + model_section + '.red' |
| 88 | |
| 89 | try: |
| 90 | f_out = open(outfile_name, 'w') |
| 91 | except OSError: |
| 92 | print('Error: Cannot write file ' + outfile_name + '.') |
| 93 | sys.exit(2) |
| 94 | |
| 95 | is_warning = False |
| 96 | process_file(infile_name, True) |
| 97 | f_out.close() |
| 98 | |
| 99 | print() |
| 100 | print('Model file ' + outfile_name + ' written.') |
| 101 | if is_warning == True: |
| 102 | print('There have been warnings! Please check output log.') |
| 103 | sys.exit(1) |
| 104 | else: |
| 105 | sys.exit(0) |
| 106 | else: |
| 107 | print() |
| 108 | print('spice_model_red.py SPICE model file reducer') |
| 109 | print(' (c) 2021 Harald Pretl, JKU') |
| 110 | print() |
| 111 | print('Usage: spice_model_red <inputfile> [corner] (default corner = tt)') |
| 112 | print() |
| 113 | print('Return codes for script automation:') |
| 114 | print(' 0 = all OK') |
| 115 | print(' 1 = warnings') |
| 116 | print(' 2 = errors') |
| 117 | print(' 3 = call of script w/o parameters (= showing this message)') |
| 118 | print() |
| 119 | sys.exit(3) |