blob: ddb4e4d86ab8c8a1f6e76342fc230d29cc17d02d [file] [log] [blame]
Tim Edwards003f2b12021-06-04 21:14:41 -04001#!/usr/bin/env python3
2#
3# Script to read a GDS file, and replace a single cell structure with a cell of
4# the same name from a different GDS file. If a checksum is provided, then
5# the cell contents will be checked against the checksum before allowing the
6# replacement. The checksum is just the sum of the length of all GDS records in
7# the cell. A checksum can be determined by running this routine once without
8# supplying a checksum; the checksum will be calculated and printed.
9#
10# There are no checks to ensure that the replacement cell is in any way compatible
11# with the existing cell. Validation must be done independently. This script is
12# only a simple GDS data compositor.
13
14import os
15import sys
16
17def usage():
18 print('change_gds_cell.py <cell_name> <path_to_cell_gds> <path_to_gds_in> [<path_to_gds_out>] [-checksum=<checksum>]')
19
20if __name__ == '__main__':
21 debug = False
22
23 if len(sys.argv) == 1:
24 print("No options given to change_gds_cell.py.")
25 usage()
26 sys.exit(0)
27
28 optionlist = []
29 arguments = []
30
31 for option in sys.argv[1:]:
32 if option.find('-', 0) == 0:
33 optionlist.append(option)
34 else:
35 arguments.append(option)
36
37 if len(arguments) < 3 or len(arguments) > 4:
38 print("Wrong number of arguments given to change_gds_cell.py.")
39 usage()
40 sys.exit(0)
41
42 checksum = '0'
43 for option in optionlist:
44 if option == '-debug':
45 debug = True
46 elif option.split('=')[0] == '-checksum':
47 checksum = option.split('=')[1]
48
49 try:
50 checksum = int(checksum)
51 except:
52 print('Checksum must evaluate to an integer.')
53 sys.exit(1)
54
55 cellname = arguments[0]
56 cellsource = arguments[1]
57 source = arguments[2]
58
59 # If only three arguments are provided, then overwrite the source file.
60 if len(arguments) == 4:
61 dest = arguments[3]
62 else:
63 dest = arguments[2]
64
65 cellsrcdir = os.path.split(cellsource)[0]
66 cellinfile = os.path.split(cellsource)[1]
67 sourcedir = os.path.split(source)[0]
68 gdsinfile = os.path.split(source)[1]
69
70 destdir = os.path.split(dest)[0]
71 gdsoutfile = os.path.split(dest)[1]
72
73 print('Reading GDS file for alternate cell ' + cellname)
74 with open(cellsource, 'rb') as ifile:
75 celldata = ifile.read()
76
77 #----------------------------------------------------------------------
78 # Assume that celldata contains the cell in question.
79 # Find the extend of the data from 'beginstr' to 'endstr'
80 #----------------------------------------------------------------------
81
82 datalen = len(celldata)
83 dataptr = 0
84 incell = False
85 datastart = dataend = -1
86 while dataptr < datalen:
87 # Read stream records up to 'beginstr', then save data through 'endstr'
88 bheader = celldata[dataptr:dataptr + 2]
89 reclen = int.from_bytes(bheader, 'big')
90 if reclen == 0:
91 print('Error: found zero-length record at position ' + str(dataptr))
92 break
93
94 rectype = celldata[dataptr + 2]
95 datatype = celldata[dataptr + 3]
96
97 brectype = rectype.to_bytes(1, byteorder='big')
98 bdatatype = datatype.to_bytes(1, byteorder='big')
99
100 if rectype == 5: # beginstr
101 saveptr = dataptr
102
103 elif rectype == 6: # strname
104 if datatype != 6:
105 print('Error: Structure name record is not a string!')
106 sys.exit(1)
107
108 bstring = celldata[dataptr + 4: dataptr + reclen]
Tim Edwards865f36f2021-06-05 10:28:45 -0400109 # Odd length strings end in null byte which needs to be removed
110 if bstring[-1] == 0:
111 bstring = bstring[:-1]
Tim Edwards003f2b12021-06-04 21:14:41 -0400112 strname = bstring.decode('ascii')
113 if strname == cellname:
114 print('Cell ' + cellname + ' found at position ' + str(saveptr))
115 datastart = saveptr
116 incell = True
117 elif debug:
118 print('Cell ' + strname + ' position ' + str(dataptr) + ' (ignored)')
119
120 elif rectype == 7: # endstr
121 if incell:
122 incell = False
123 dataend = dataptr + reclen
124 print('Cell ' + cellname + ' ends at position ' + str(dataend))
125
126 # Advance the pointer past the data
127 dataptr += reclen
128
129 if datastart == -1 or dataend == -1:
130 print('Failed to find the cell data for ' + cellname)
131 sys.exit(1)
132
133 #-----------------------------------------------------------------
134 # Now do the same thing for the source GDS file.
135 #-----------------------------------------------------------------
136
137 print('Reading GDS file for original source ' + source)
138 with open(source, 'rb') as ifile:
139 gdsdata = ifile.read()
140
141 datalen = len(gdsdata)
142 dataptr = 0
143 incell = False
144 cellchecksum = 0
145 oldstart = oldend = -1
146 while dataptr < datalen:
147 # Read stream records up to any structure, then check for structure name
148 bheader = gdsdata[dataptr:dataptr + 2]
149 reclen = int.from_bytes(bheader, 'big')
150 if reclen == 0:
151 print('Error: found zero-length record at position ' + str(dataptr))
152 break
153
154 rectype = gdsdata[dataptr + 2]
155 datatype = gdsdata[dataptr + 3]
156
157 brectype = rectype.to_bytes(1, byteorder='big')
158 bdatatype = datatype.to_bytes(1, byteorder='big')
159
160 if rectype == 5: # beginstr
161 saveptr = dataptr
162
163 elif rectype == 6: # strname
164 if datatype != 6:
165 print('Error: Structure name record is not a string!')
166 sys.exit(1)
167
168 bstring = gdsdata[dataptr + 4: dataptr + reclen]
Tim Edwards865f36f2021-06-05 10:28:45 -0400169 # Odd length strings end in null byte which needs to be removed
170 if bstring[-1] == 0:
171 bstring = bstring[:-1]
Tim Edwards003f2b12021-06-04 21:14:41 -0400172 strname = bstring.decode('ascii')
173 if strname == cellname:
174 print('Cell ' + cellname + ' found at position ' + str(saveptr))
175 oldstart = saveptr
176 incell = True
177 elif debug:
178 print('Cell ' + strname + ' position ' + str(dataptr) + ' (copied)')
179
180 elif rectype == 7: # endstr
181 if incell:
182 incell = False
183 cellchecksum = cellchecksum + reclen
184 oldend = dataptr + reclen
185 print('Cell ' + cellname + ' ends at position ' + str(oldend))
186 print('Cell ' + cellname + ' checksum is ' + str(cellchecksum))
187
188 # Find checksum (sum of length of all records in the cell of interest)
189 if incell:
190 cellchecksum = cellchecksum + reclen
191
192 # Advance the pointer past the data
193 dataptr += reclen
194
195 if oldstart == -1 or oldend == -1:
196 print('Failed to find the cell data for ' + cellname)
197 sys.exit(1)
198
199 if checksum != 0:
200 if cellchecksum == checksum:
201 print('Info: Structure ' + cellname + ' matches checksum ' + str(checksum))
202 else:
203 print('Info: Structure ' + cellname + ' at ' + str(oldstart) + ' to ' +
204 str(oldend) + ' has checksum ' + str(cellchecksum) +
205 ' != ' + str(checksum) + ' (checksum failure)')
206 sys.exit(1)
207 else:
208 print('Info: Structure ' + cellname + ' checksum is ' + str(cellchecksum))
209
210 print('Info: Structure ' + cellname + ' at ' + str(oldstart) + ' to ' +
211 str(oldend) + ' will be replaced by alternate data.')
212
213 before = gdsdata[0:oldstart]
214 after = gdsdata[oldend:]
215
216 cellstrdata = celldata[datastart:dataend]
217
218 # Reassemble the GDS data around the new cell
219 gdsdata = before + cellstrdata + after
220
221 with open(dest, 'wb') as ofile:
222 ofile.write(gdsdata)
223
224 exit(0)