Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # create_gds_library.py |
| 4 | # |
| 5 | #---------------------------------------------------------------------------- |
| 6 | # Given a destination directory holding individual GDS files of a number |
| 7 | # of cells, create a single GDL library file named <alllibname> and place |
| 8 | # it in the same directory. This is done for the option "compile" if specified |
| 9 | # for the "-gds" install. |
| 10 | #---------------------------------------------------------------------------- |
| 11 | |
| 12 | import os |
| 13 | import sys |
| 14 | import glob |
| 15 | import fnmatch |
| 16 | import subprocess |
| 17 | |
| 18 | #---------------------------------------------------------------------------- |
| 19 | |
| 20 | def usage(): |
| 21 | print('') |
| 22 | print('Usage:') |
| 23 | print(' create_gds_library <destlibdir> <destlib> <startup_script> ') |
| 24 | print(' [-compile-only] [-excludelist="file1,file2,..."] [-keep]') |
| 25 | print('') |
| 26 | print('Create a single GDS library from a set of individual GDS files.') |
| 27 | print('') |
| 28 | print('where:') |
| 29 | print(' <destlibdir> is the directory containing the individual GDS files') |
| 30 | print(' <destlib> is the root name of the library file') |
| 31 | print(' <startup_script> is the full path to a magic startup script') |
| 32 | print(' -compile-only removes the indidual files if specified') |
| 33 | print(' -excludelist= is a comma-separated list of files to ignore') |
| 34 | print(' -keep keep the Tcl script used to generate the library') |
| 35 | print('') |
| 36 | |
| 37 | #---------------------------------------------------------------------------- |
| 38 | |
Tim Edwards | 9be4ac2 | 2020-07-26 12:59:30 -0400 | [diff] [blame] | 39 | def create_gds_library(destlibdir, destlib, startup_script, do_compile_only=False, excludelist=[], keep=False): |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 40 | |
Tim Edwards | 05e66eb | 2020-09-24 13:11:59 -0400 | [diff] [blame] | 41 | # destlib should not have a file extension |
| 42 | destlibroot = os.path.splitext(destlib)[0] |
| 43 | |
| 44 | alllibname = destlibdir + '/' + destlibroot + '.gds' |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 45 | if os.path.isfile(alllibname): |
| 46 | os.remove(alllibname) |
| 47 | |
Tim Edwards | 995c133 | 2020-09-25 15:33:58 -0400 | [diff] [blame] | 48 | # If file "filelist.txt" exists in the directory, get the list of files from it |
| 49 | if os.path.exists(destlibdir + '/filelist.txt'): |
| 50 | with open(destlibdir + '/filelist.txt', 'r') as ifile: |
| 51 | rlist = ifile.read().splitlines() |
| 52 | glist = [] |
| 53 | for rfile in rlist: |
| 54 | glist.append(destlibdir + '/' + rfile) |
| 55 | else: |
| 56 | glist = glob.glob(destlibdir + '/*.gds') |
| 57 | glist.extend(glob.glob(destlibdir + '/*.gdsii')) |
| 58 | glist.extend(glob.glob(destlibdir + '/*.gds2')) |
| 59 | |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 60 | if alllibname in glist: |
| 61 | glist.remove(alllibname) |
| 62 | |
| 63 | # Create exclude list with glob-style matching using fnmatch |
| 64 | if len(glist) > 0: |
| 65 | glistnames = list(os.path.split(item)[1] for item in glist) |
| 66 | notglist = [] |
| 67 | for exclude in excludelist: |
| 68 | notglist.extend(fnmatch.filter(glistnames, exclude)) |
| 69 | |
| 70 | # Apply exclude list |
| 71 | if len(notglist) > 0: |
| 72 | for file in glist[:]: |
| 73 | if os.path.split(file)[1] in notglist: |
| 74 | glist.remove(file) |
| 75 | |
| 76 | if len(glist) > 1: |
| 77 | print('New file is: ' + alllibname) |
| 78 | |
| 79 | if os.path.isfile(startup_script): |
| 80 | # If the symbolic link exists, remove it. |
| 81 | if os.path.isfile(destlibdir + '/.magicrc'): |
| 82 | os.remove(destlibdir + '/.magicrc') |
| 83 | os.symlink(startup_script, destlibdir + '/.magicrc') |
| 84 | |
| 85 | # A GDS library is binary and requires handling in Magic |
| 86 | print('Creating magic generation script to generate GDS library.') |
| 87 | with open(destlibdir + '/generate_magic.tcl', 'w') as ofile: |
| 88 | print('#!/usr/bin/env wish', file=ofile) |
| 89 | print('#--------------------------------------------', file=ofile) |
| 90 | print('# Script to generate .gds library from files ', file=ofile) |
| 91 | print('#--------------------------------------------', file=ofile) |
| 92 | print('drc off', file=ofile) |
| 93 | print('gds readonly true', file=ofile) |
| 94 | print('gds flatten true', file=ofile) |
| 95 | print('gds rescale false', file=ofile) |
| 96 | print('tech unlock *', file=ofile) |
| 97 | |
| 98 | for gdsfile in glist: |
| 99 | print('gds read ' + gdsfile, file=ofile) |
| 100 | |
Tim Edwards | 05e66eb | 2020-09-24 13:11:59 -0400 | [diff] [blame] | 101 | print('puts stdout "Creating cell ' + destlibroot + '"', file=ofile) |
| 102 | print('load ' + destlibroot, file=ofile) |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 103 | print('puts stdout "Adding cells to library"', file=ofile) |
| 104 | print('box values 0 0 0 0', file=ofile) |
| 105 | for gdsfile in glist: |
| 106 | gdsroot = os.path.split(gdsfile)[1] |
| 107 | gdsname = os.path.splitext(gdsroot)[0] |
| 108 | print('getcell ' + gdsname, file=ofile) |
| 109 | # Could properly make space for the cell here. . . |
| 110 | print('box move e 200', file=ofile) |
| 111 | |
Tim Edwards | 05e66eb | 2020-09-24 13:11:59 -0400 | [diff] [blame] | 112 | print('puts stdout "Writing GDS library ' + destlibroot + '"', file=ofile) |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 113 | print('gds library true', file=ofile) |
Tim Edwards | 05e66eb | 2020-09-24 13:11:59 -0400 | [diff] [blame] | 114 | print('gds write ' + destlibroot, file=ofile) |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 115 | print('puts stdout "Done."', file=ofile) |
| 116 | print('quit -noprompt', file=ofile) |
| 117 | |
| 118 | # Run magic to read in the individual GDS files and |
| 119 | # write out the consolidated GDS library |
| 120 | |
| 121 | print('Running magic to create GDS library.') |
| 122 | sys.stdout.flush() |
| 123 | |
| 124 | mproc = subprocess.run(['magic', '-dnull', '-noconsole', |
| 125 | destlibdir + '/generate_magic.tcl'], |
| 126 | stdin = subprocess.DEVNULL, |
| 127 | stdout = subprocess.PIPE, |
| 128 | stderr = subprocess.PIPE, cwd = destlibdir, |
| 129 | universal_newlines = True) |
| 130 | |
| 131 | if mproc.stdout: |
| 132 | for line in mproc.stdout.splitlines(): |
| 133 | print(line) |
| 134 | if mproc.stderr: |
| 135 | print('Error message output from magic:') |
| 136 | for line in mproc.stderr.splitlines(): |
| 137 | print(line) |
| 138 | if mproc.returncode != 0: |
| 139 | print('ERROR: Magic exited with status ' + str(mproc.returncode)) |
| 140 | if do_compile_only == True: |
| 141 | print('Compile-only: Removing individual GDS files') |
| 142 | for gfile in glist: |
| 143 | if os.path.isfile(gfile): |
| 144 | os.remove(gfile) |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 145 | if not keep: |
| 146 | os.remove(destlibdir + '/generate_magic.tcl') |
| 147 | else: |
| 148 | print('Only one file (' + str(glist) + '); ignoring "compile" option.') |
| 149 | |
| 150 | #---------------------------------------------------------------------------- |
| 151 | |
| 152 | if __name__ == '__main__': |
| 153 | |
| 154 | if len(sys.argv) == 1: |
| 155 | usage() |
| 156 | sys.exit(0) |
| 157 | |
| 158 | argumentlist = [] |
| 159 | |
| 160 | # Defaults |
| 161 | do_compile_only = False |
| 162 | keep = False |
| 163 | excludelist = [] |
| 164 | |
| 165 | # Break arguments into groups where the first word begins with "-". |
| 166 | # All following words not beginning with "-" are appended to the |
| 167 | # same list (optionlist). Then each optionlist is processed. |
| 168 | # Note that the first entry in optionlist has the '-' removed. |
| 169 | |
| 170 | for option in sys.argv[1:]: |
| 171 | if option.find('-', 0) == 0: |
| 172 | keyval = option[1:].split('=') |
| 173 | if keyval[0] == 'compile-only': |
| 174 | if len(keyval) > 0: |
Tim Edwards | 9be4ac2 | 2020-07-26 12:59:30 -0400 | [diff] [blame] | 175 | if keyval[1].tolower() == 'true' or keyval[1].tolower() == 'yes' or keyval[1] == '1': |
Tim Edwards | 51f8142 | 2020-07-26 12:49:48 -0400 | [diff] [blame] | 176 | do_compile_only = True |
| 177 | else: |
| 178 | do_compile_only = True |
| 179 | elif keyval[1] == 'exclude' or key == 'excludelist': |
| 180 | if len(keyval) > 0: |
| 181 | excludelist = keyval[1].trim('"').split(',') |
| 182 | else: |
| 183 | print("No items in exclude list (ignoring).") |
| 184 | elif keyval[1] == 'keep': |
| 185 | keep = True |
| 186 | else: |
| 187 | print("Unknown option '" + keyval[0] + "' (ignoring).") |
| 188 | else: |
| 189 | argumentlist.append(option) |
| 190 | |
| 191 | if len(argumentlist) < 3: |
| 192 | print("Not enough arguments given to create_gds_library.py.") |
| 193 | usage() |
| 194 | sys.exit(1) |
| 195 | |
| 196 | destlibdir = argumentlist[0] |
| 197 | destlib = argumentlist[1] |
| 198 | startup_script = argumentlist[2] |
| 199 | |
| 200 | print('') |
| 201 | print('Create GDS library from files:') |
| 202 | print('') |
| 203 | print('Path to files: ' + destlibdir) |
| 204 | print('Name of compiled library: ' + destlib + '.gds') |
| 205 | print('Path to magic startup script: ' + startup_script) |
| 206 | print('Remove individual files: ' + 'Yes' if do_compile_only else 'No') |
| 207 | if len(excludelist) > 0: |
| 208 | print('List of files to exclude: ') |
| 209 | for file in excludelist: |
| 210 | print(file) |
| 211 | print('Keep generating script: ' + 'Yes' if keep else 'No') |
| 212 | print('') |
| 213 | |
| 214 | create_gds_library(destlibdir, destlib, startup_script, do_compile_only, excludelist, keep) |
| 215 | print('Done.') |
| 216 | sys.exit(0) |
| 217 | |
| 218 | #---------------------------------------------------------------------------- |