blob: 7e1b65aa683db567505b262d59fd8055d2fa7976 [file] [log] [blame]
Tim Edwardsc5522922020-11-30 16:56:58 -05001#!/bin/env python3
agorararmarde5780bf2020-12-09 21:27:56 +00002# Copyright 2020 Efabless Corporation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
agorararmardafa96ea2020-12-09 23:37:31 +020015# SPDX-License-Identifier: Apache-2.0
agorararmarde5780bf2020-12-09 21:27:56 +000016
Tim Edwardsc5522922020-11-30 16:56:58 -050017#
18# set_user_id.py ---
19#
20# Manipulate the magic database, GDS, and verilog source files for the
21# user_id_programming block to set the user ID number.
22#
23# The user ID number is a 32-bit value that is passed to this routine
24# as an integer.
25#
26# user_id_programming layout map:
27# Positions marked (in microns) for value = 0. For value = 1, move
28# the via 0.92um to the left.
29#
30# Layout grid is 0.46um x 0.34um with half-pitch offset (0.23um, 0.17um)
31#
32# Signal Via position (um)
33# name X Y
34#--------------------------------
35# mask_rev[0] 14.49 9.35
36# mask_rev[1] 16.33 9.35
37# mask_rev[2] 10.35 20.23
38# mask_rev[3] 8.05 9.35
39# mask_rev[4] 28.29 9.35
40# mask_rev[5] 21.85 25.67
41# mask_rev[6] 8.05 20.23
42# mask_rev[7] 20.47 9.35
43# mask_rev[8] 17.25 17.85
44# mask_rev[9] 25.53 12.07
45# mask_rev[10] 22.31 20.23
46# mask_rev[11] 13.11 9.35
47# mask_rev[12] 23.69 23.29
48# mask_rev[13] 24.15 12.07
49# mask_rev[14] 13.57 17.85
50# mask_rev[15] 23.23 6.97
51# mask_rev[16] 24.15 17.85
52# mask_rev[17] 8.51 17.85
53# mask_rev[18] 23.69 20.23
54# mask_rev[19] 10.81 23.29
55# mask_rev[20] 14.95 6.97
56# mask_rev[21] 18.17 23.29
57# mask_rev[22] 21.39 17.85
58# mask_rev[23] 26.45 25.67
59# mask_rev[24] 9.89 17.85
60# mask_rev[25] 15.87 17.85
61# mask_rev[26] 26.45 17.85
62# mask_rev[27] 8.51 6.97
63# mask_rev[28] 10.81 9.35
64# mask_rev[29] 27.83 20.23
65# mask_rev[30] 16.33 23.29
66# mask_rev[31] 8.05 14.79
67#--------------------------------
68
69import os
70import sys
71import re
72
73def usage():
74 print("set_user_id.py <user_id_value> [<path_to_project>]")
75 return 0
76
77if __name__ == '__main__':
78
79 # Coordinate pairs in microns for the zero position on each bit
80 mask_rev = (
81 (14.49, 9.35), (16.33, 9.35), (10.35, 20.23), ( 8.05, 9.35),
82 (28.29, 9.35), (21.85, 25.67), ( 8.05, 20.23), (20.47, 9.35),
83 (17.25, 17.85), (25.53, 12.07), (22.31, 20.23), (13.11, 9.35),
84 (23.69, 23.29), (24.15, 12.07), (13.57, 17.85), (23.23, 6.97),
85 (24.15, 17.85), ( 8.51, 17.85), (23.69, 20.23), (10.81, 23.29),
86 (14.95, 6.97), (18.17, 23.29), (21.39, 17.85), (26.45, 25.67),
87 ( 9.89, 17.85), (15.87, 17.85), (26.45, 17.85), ( 8.51, 6.97),
88 (10.81, 9.35), (27.83, 20.23), (16.33, 23.29), ( 8.05, 14.79));
89
90 optionlist = []
91 arguments = []
92
93 debugmode = False
94
95 for option in sys.argv[1:]:
96 if option.find('-', 0) == 0:
97 optionlist.append(option)
98 else:
99 arguments.append(option)
100
101 if len(arguments) != 1 and len(arguments) != 2:
102 if len(arguments) != 0:
103 print("Wrong number of arguments given to cleanup_unref.py.")
104 usage()
105 sys.exit(0)
106
107 if '-debug' in optionlist:
108 debugmode = True
109
110 user_id_value = arguments[0]
111
112 # Convert to binary
113 user_id_bits = '{0:032b}'.format(int(user_id_value))
114
115 if len(arguments) == 2:
116 user_project_path = arguments[1]
117 else:
118 user_project_path = os.getcwd()
119
120 magpath = user_project_path + '/mag'
121 gdspath = user_project_path + '/gds'
122 vpath = user_project_path + '/verilog'
123 errors = 0
124
125 if os.path.isdir(gdspath):
126
127 # Bytes leading up to via position are:
128 viarec = "00 06 0d 02 00 43 00 06 0e 02 00 2c 00 2c 10 03 "
129 viabytes = bytes.fromhex(viarec)
130
131 # Read the GDS file. If a backup was made of the zero-value
132 # program, then use it.
133
134 gdsbak = gdspath + '/user_id_prog_zero.gds'
135 gdsfile = gdspath + '/user_id_programming.gds'
136
137 if os.path.isfile(gdsbak):
138 with open(gdsbak, 'rb') as ifile:
139 gdsdata = ifile.read()
140 else:
141 with open(gdsfile, 'rb') as ifile:
142 gdsdata = ifile.read()
143
144 for i in range(0,32):
145 # Ignore any zero bits.
146 if user_id_bits[i] == '0':
147 continue
148
149 coords = mask_rev[i]
150 xum = coords[0]
151 yum = coords[1]
152
153 # Contact is 0.17 x 0.17, so add and subtract 0.085 to get
154 # the corner positions.
155
156 xllum = xum - 0.085
157 yllum = yum - 0.085
158 xurum = xum + 0.085
159 yurum = yum + 0.085
160
161 # Get the 4-byte hex values for the corner coordinates
162 xllnm = round(xllum * 1000)
163 yllnm = round(yllum * 1000)
164 xllhex = '{0:08x}'.format(xllnm)
165 yllhex = '{0:08x}'.format(yllnm)
166 xurnm = round(xurum * 1000)
167 yurnm = round(yurum * 1000)
168 xurhex = '{0:08x}'.format(xurnm)
169 yurhex = '{0:08x}'.format(yurnm)
170
171 # Magic's GDS output for vias always starts at the lower left
172 # corner and goes counterclockwise, repeating the first point.
173 viaoldposdata = viarec + xllhex + yllhex + xurhex + yllhex
174 viaoldposdata += xurhex + yurhex + xllhex + yurhex + xllhex + yllhex
175
176 # For "one" bits, the X position is moved 0.92 microns to the left
177 newxllum = xllum - 0.92
178 newxurum = xurum - 0.92
179
180 # Get the 4-byte hex values for the new corner coordinates
181 newxllnm = round(newxllum * 1000)
182 newxllhex = '{0:08x}'.format(newxllnm)
183 newxurnm = round(newxurum * 1000)
184 newxurhex = '{0:08x}'.format(newxurnm)
185
186 vianewposdata = viarec + newxllhex + yllhex + newxurhex + yllhex
187 vianewposdata += newxurhex + yurhex + newxllhex + yurhex + newxllhex + yllhex
188
189 # Diagnostic
190 if debugmode:
191 print('Bit ' + str(i) + ':')
192 print('Via position ({0:3.2f}, {1:3.2f}) to ({2:3.2f}, {3:3.2f})'.format(xllum, yllum, xurum, yurum))
193 print('Old hex string = ' + viaoldposdata)
194 print('New hex string = ' + vianewposdata)
195
196 # Convert hex strings to byte arrays
197 viaoldbytedata = bytearray.fromhex(viaoldposdata)
198 vianewbytedata = bytearray.fromhex(vianewposdata)
199
200 # Replace the old data with the new
201 if viaoldbytedata not in gdsdata:
202 print('Error: via not found for bit position ' + str(i))
203 errors += 1
204 else:
205 gdsdata = gdsdata.replace(viaoldbytedata, vianewbytedata)
206
207 if errors == 0:
208 # Keep a copy of the original
209 if not os.path.isfile(gdsbak):
210 os.rename(gdsfile, gdsbak)
211
212 with open(gdsfile, 'wb') as ofile:
213 ofile.write(gdsdata)
214
215 print('Done!')
216
217 else:
218 print('There were errors in processing. No file written.')
219 sys.exit(1)
220
221 else:
222 print('No directory ' + gdspath + ' found.')
223 sys.exit(1)
224 sys.exit(0)