| #!/usr/bin/env python3 |
| # |
| # Script to read a GDS file, and replace a single cell structure with a cell of |
| # the same name from a different GDS file. If a checksum is provided, then |
| # the cell contents will be checked against the checksum before allowing the |
| # replacement. The checksum is just the sum of the length of all GDS records in |
| # the cell. A checksum can be determined by running this routine once without |
| # supplying a checksum; the checksum will be calculated and printed. |
| # |
| # There are no checks to ensure that the replacement cell is in any way compatible |
| # with the existing cell. Validation must be done independently. This script is |
| # only a simple GDS data compositor. |
| |
| import os |
| import sys |
| |
| def usage(): |
| print('change_gds_cell.py <cell_name> <path_to_cell_gds> <path_to_gds_in> [<path_to_gds_out>] [-checksum=<checksum>]') |
| |
| if __name__ == '__main__': |
| debug = False |
| |
| if len(sys.argv) == 1: |
| print("No options given to change_gds_cell.py.") |
| usage() |
| sys.exit(0) |
| |
| optionlist = [] |
| arguments = [] |
| |
| for option in sys.argv[1:]: |
| if option.find('-', 0) == 0: |
| optionlist.append(option) |
| else: |
| arguments.append(option) |
| |
| if len(arguments) < 3 or len(arguments) > 4: |
| print("Wrong number of arguments given to change_gds_cell.py.") |
| usage() |
| sys.exit(0) |
| |
| checksum = '0' |
| for option in optionlist: |
| if option == '-debug': |
| debug = True |
| elif option.split('=')[0] == '-checksum': |
| checksum = option.split('=')[1] |
| |
| try: |
| checksum = int(checksum) |
| except: |
| print('Checksum must evaluate to an integer.') |
| sys.exit(1) |
| |
| cellname = arguments[0] |
| cellsource = arguments[1] |
| source = arguments[2] |
| |
| # If only three arguments are provided, then overwrite the source file. |
| if len(arguments) == 4: |
| dest = arguments[3] |
| else: |
| dest = arguments[2] |
| |
| cellsrcdir = os.path.split(cellsource)[0] |
| cellinfile = os.path.split(cellsource)[1] |
| sourcedir = os.path.split(source)[0] |
| gdsinfile = os.path.split(source)[1] |
| |
| destdir = os.path.split(dest)[0] |
| gdsoutfile = os.path.split(dest)[1] |
| |
| print('Reading GDS file for alternate cell ' + cellname) |
| with open(cellsource, 'rb') as ifile: |
| celldata = ifile.read() |
| |
| #---------------------------------------------------------------------- |
| # Assume that celldata contains the cell in question. |
| # Find the extend of the data from 'beginstr' to 'endstr' |
| #---------------------------------------------------------------------- |
| |
| datalen = len(celldata) |
| dataptr = 0 |
| incell = False |
| datastart = dataend = -1 |
| while dataptr < datalen: |
| # Read stream records up to 'beginstr', then save data through 'endstr' |
| bheader = celldata[dataptr:dataptr + 2] |
| reclen = int.from_bytes(bheader, 'big') |
| if reclen == 0: |
| print('Error: found zero-length record at position ' + str(dataptr)) |
| break |
| |
| rectype = celldata[dataptr + 2] |
| datatype = celldata[dataptr + 3] |
| |
| brectype = rectype.to_bytes(1, byteorder='big') |
| bdatatype = datatype.to_bytes(1, byteorder='big') |
| |
| if rectype == 5: # beginstr |
| saveptr = dataptr |
| |
| elif rectype == 6: # strname |
| if datatype != 6: |
| print('Error: Structure name record is not a string!') |
| sys.exit(1) |
| |
| bstring = celldata[dataptr + 4: dataptr + reclen] |
| # Odd length strings end in null byte which needs to be removed |
| if bstring[-1] == 0: |
| bstring = bstring[:-1] |
| strname = bstring.decode('ascii') |
| if strname == cellname: |
| print('Cell ' + cellname + ' found at position ' + str(saveptr)) |
| datastart = saveptr |
| incell = True |
| elif debug: |
| print('Cell ' + strname + ' position ' + str(dataptr) + ' (ignored)') |
| |
| elif rectype == 7: # endstr |
| if incell: |
| incell = False |
| dataend = dataptr + reclen |
| print('Cell ' + cellname + ' ends at position ' + str(dataend)) |
| |
| # Advance the pointer past the data |
| dataptr += reclen |
| |
| if datastart == -1 or dataend == -1: |
| print('Failed to find the cell data for ' + cellname) |
| sys.exit(1) |
| |
| #----------------------------------------------------------------- |
| # Now do the same thing for the source GDS file. |
| #----------------------------------------------------------------- |
| |
| print('Reading GDS file for original source ' + source) |
| with open(source, 'rb') as ifile: |
| gdsdata = ifile.read() |
| |
| datalen = len(gdsdata) |
| dataptr = 0 |
| incell = False |
| cellchecksum = 0 |
| oldstart = oldend = -1 |
| while dataptr < datalen: |
| # Read stream records up to any structure, then check for structure name |
| bheader = gdsdata[dataptr:dataptr + 2] |
| reclen = int.from_bytes(bheader, 'big') |
| if reclen == 0: |
| print('Error: found zero-length record at position ' + str(dataptr)) |
| break |
| |
| rectype = gdsdata[dataptr + 2] |
| datatype = gdsdata[dataptr + 3] |
| |
| brectype = rectype.to_bytes(1, byteorder='big') |
| bdatatype = datatype.to_bytes(1, byteorder='big') |
| |
| if rectype == 5: # beginstr |
| saveptr = dataptr |
| |
| elif rectype == 6: # strname |
| if datatype != 6: |
| print('Error: Structure name record is not a string!') |
| sys.exit(1) |
| |
| bstring = gdsdata[dataptr + 4: dataptr + reclen] |
| # Odd length strings end in null byte which needs to be removed |
| if bstring[-1] == 0: |
| bstring = bstring[:-1] |
| strname = bstring.decode('ascii') |
| if strname == cellname: |
| print('Cell ' + cellname + ' found at position ' + str(saveptr)) |
| oldstart = saveptr |
| incell = True |
| elif debug: |
| print('Cell ' + strname + ' position ' + str(dataptr) + ' (copied)') |
| |
| elif rectype == 7: # endstr |
| if incell: |
| incell = False |
| cellchecksum = cellchecksum + reclen |
| oldend = dataptr + reclen |
| print('Cell ' + cellname + ' ends at position ' + str(oldend)) |
| print('Cell ' + cellname + ' checksum is ' + str(cellchecksum)) |
| |
| # Find checksum (sum of length of all records in the cell of interest) |
| if incell: |
| cellchecksum = cellchecksum + reclen |
| |
| # Advance the pointer past the data |
| dataptr += reclen |
| |
| if oldstart == -1 or oldend == -1: |
| print('Failed to find the cell data for ' + cellname) |
| sys.exit(1) |
| |
| if checksum != 0: |
| if cellchecksum == checksum: |
| print('Info: Structure ' + cellname + ' matches checksum ' + str(checksum)) |
| else: |
| print('Info: Structure ' + cellname + ' at ' + str(oldstart) + ' to ' + |
| str(oldend) + ' has checksum ' + str(cellchecksum) + |
| ' != ' + str(checksum) + ' (checksum failure)') |
| sys.exit(1) |
| else: |
| print('Info: Structure ' + cellname + ' checksum is ' + str(cellchecksum)) |
| |
| print('Info: Structure ' + cellname + ' at ' + str(oldstart) + ' to ' + |
| str(oldend) + ' will be replaced by alternate data.') |
| |
| before = gdsdata[0:oldstart] |
| after = gdsdata[oldend:] |
| |
| cellstrdata = celldata[datastart:dataend] |
| |
| # Reassemble the GDS data around the new cell |
| gdsdata = before + cellstrdata + after |
| |
| with open(dest, 'wb') as ofile: |
| ofile.write(gdsdata) |
| |
| exit(0) |