blob: 3cb9e7a90060abac54884f66919f3a1012c4a366 [file] [log] [blame]
Donn6032c992021-05-04 17:43:13 +02001#!/usr/bin/env python3
2# Copyright 2020 R. Timothy Edwards
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# https://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.
15#
16# SPDX-License-Identifier: Apache-2.0
Tim Edwardsc46b9532021-04-12 14:31:29 -040017#-------------------------------------------------------------------------
18# run_standard_drc.py --- A script to run magic in batch mode and apply
19# full DRC checks on a layout. This inclues full DRC but excludes
20# antenna and density checks, for which there are separate scripts.
21#
22# Usage:
23#
24# run_standard_drc.py <layout_name>
25#
26# Results:
27#
28# generates a file "<layout_name>_drc.txt" containing a human-readable
29# list of the DRC errors.
30#
31#-------------------------------------------------------------------------
32
33import subprocess
34import shutil
35import glob
36import sys
37import os
38import re
39
40# Work in progress
41
42def run_full_drc(layout_name, output_file):
43 is_gds = False
44
45 # Remove any extension from layout_name
46 layout_root = layout_name
47 layout_name = os.path.splitext(layout_root)[0]
48 layout_ext = os.path.splitext(layout_root)[1]
49
50 if layout_ext != '.mag':
51 # Assume this is GDS
52 # Is the layout file in the current directory, or a full
53 # path, or is this a project directory?
54
55 is_gds = True
56 if layout_name[0] == '/':
57 gdspath = os.path.split(layout_name)[0]
58 layout_name = os.path.split(layout_name)[1]
59
60 else:
61 if not os.path.isfile(layout_root):
62 if not os.path.isfile('gds/' + layout_root):
63 print('Error: Cannot find GDS file ' + layout_root)
64 return
65 else:
66 gdspath = os.getcwd() + '/gds'
67 else:
68 gdspath = os.getcwd()
69
70 if os.path.isdir('mag/'):
71 magpath = os.getcwd() + '/mag'
72 else:
73 magpath = os.getcwd()
74
75 else:
76 # File is a .mag layout
77 # Is the layout file in the current directory, or a full
78 # path, or is this a project directory?
79
80 if layout_name[0] == '/':
81 magpath = os.path.split(layout_name)[0]
82 layout_name = os.path.split(layout_name)[1]
83
84 else:
85 if not os.path.isfile(layout_name + '.mag'):
86 if not os.path.isfile('mag/' + layout_name + '.mag'):
87 print('Error: Cannot find file ' + layout_name + '.mag')
88 return
89 else:
90 magpath = os.getcwd() + '/mag'
91 else:
92 magpath = os.getcwd()
93
94 if output_file == '':
95 output_file = layout_name + '_drc.txt'
96
97 # Check for presence of a .magicrc file, or else check for environment
98 # variable PDKPATH, or PDK_PATH
99
100 myenv = os.environ.copy()
101 myenv['MAGTYPE'] = 'mag'
102
103 if os.path.isfile(magpath + '/.magicrc'):
104 rcfile = magpath + '/.magicrc'
105 elif os.path.isfile(os.getcwd() + '/.magicrc'):
106 rcfile = os.getcwd() + '/.magicrc'
107 else:
108 if 'PDKPATH' in myenv:
109 rcpathroot = myenv['PDKPATH'] + '/libs.tech/magic'
110 rcfile = glob.glob(rcpathroot + '/*.magicrc')[0]
111 elif 'PDK_PATH' in myenv:
112 rcpathroot = myenv['PDKPATH'] + '/libs.tech/magic'
113 rcfile = glob.glob(rcpathroot + '/*.magicrc')[0]
114 else:
115 print('Error: Cannot get magic rcfile for the technology!')
116 return
117
118 # Generate the DRC Tcl script
119
Tim Edwards4bc2df22021-04-24 12:12:19 -0400120 # If magpath is writeable, then continue. If not, then if the
121 # current working directory is writeable, use it.
122 if not os.access(magpath, os.W_OK):
123 if os.access(os.getcwd(), os.W_OK):
124 scriptpath = os.getcwd()
125 # The output .txt file won't be writeable, either.
126 output_file = os.path.join(scriptpath, os.path.split(output_file)[1])
127 else:
128 print('Error: Neither the path of the layout or the current directory is writeable.')
129 return
130 else:
131 scriptpath = magpath
132
Tim Edwardsc46b9532021-04-12 14:31:29 -0400133 print('Evaluating full DRC results for layout ' + layout_name)
Donn6032c992021-05-04 17:43:13 +0200134 magic_script = scriptpath + "/run_magic_drc_%s.tcl" % os.path.basename(layout_name)
135 with open(magic_script, 'w') as ofile:
Tim Edwardsc46b9532021-04-12 14:31:29 -0400136 print('# run_magic_drc.tcl ---', file=ofile)
137 print('# batch script for running DRC', file=ofile)
138 print('', file=ofile)
139 print('crashbackups stop', file=ofile)
140 print('drc euclidean on', file=ofile)
141 print('drc style drc(full)', file=ofile)
142 print('drc on', file=ofile)
143 print('snap internal', file=ofile)
144 if is_gds:
145 print('gds flatglob *__example_*', file=ofile)
146 print('gds flatten true', file=ofile)
147 print('gds read ' + gdspath + '/' + layout_name, file=ofile)
148 print('load ' + layout_name, file=ofile)
149 else:
150 print('load ' + layout_name + ' -dereference', file=ofile)
151 print('select top cell', file=ofile)
152 print('expand', file=ofile)
153 print('drc catchup', file=ofile)
154 print('set allerrors [drc listall why]', file=ofile)
155 print('set oscale [cif scale out]', file=ofile)
156 print('set ofile [open ' + output_file + ' w]', file=ofile)
157 print('puts $ofile "DRC errors for cell ' + layout_name + '"', file=ofile)
158 print('puts $ofile "--------------------------------------------"', file=ofile)
159 print('foreach {whytext rectlist} $allerrors {', file=ofile)
160 print(' puts $ofile ""', file=ofile)
161 print(' puts $ofile $whytext', file=ofile)
162 print(' foreach rect $rectlist {', file=ofile)
163 print(' set llx [format "%.3f" [expr $oscale * [lindex $rect 0]]]',
164 file=ofile)
165 print(' set lly [format "%.3f" [expr $oscale * [lindex $rect 1]]]',
166 file=ofile)
167 print(' set urx [format "%.3f" [expr $oscale * [lindex $rect 2]]]',
168 file=ofile)
169 print(' set ury [format "%.3f" [expr $oscale * [lindex $rect 3]]]',
170 file=ofile)
171 print(' puts $ofile "$llx $lly $urx $ury"', file=ofile)
172 print(' }', file=ofile)
173 print('}', file=ofile)
174 print('close $ofile', file=ofile)
175
176 # Run the DRC Tcl script
177
Donn6032c992021-05-04 17:43:13 +0200178 print('Running: magic -dnull -noconsole -rcfile ' + rcfile + ' ' + magic_script)
Tim Edwardsc46b9532021-04-12 14:31:29 -0400179 print('Running in directory: ' + magpath)
180 mproc = subprocess.run(['magic', '-dnull', '-noconsole', '-rcfile',
Donn6032c992021-05-04 17:43:13 +0200181 rcfile, magic_script],
Tim Edwardsc46b9532021-04-12 14:31:29 -0400182 env = myenv, cwd = magpath,
183 stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
184 stderr = subprocess.PIPE, universal_newlines = True)
185 if mproc.stdout:
186 for line in mproc.stdout.splitlines():
187 print(line)
188 if mproc.stderr:
189 print('Error message output from magic:')
190 for line in mproc.stderr.splitlines():
191 print(line)
192 if mproc.returncode != 0:
193 print('ERROR: Magic exited with status ' + str(mproc.returncode))
194
195 print('Done!')
196
197# If called as main, run all DRC tests
198
199if __name__ == '__main__':
200
201 # Divide up command line into options and arguments
202 options = []
203 arguments = []
204 for item in sys.argv[1:]:
205 if item.find('-', 0) == 0:
206 options.append(item)
207 else:
208 arguments.append(item)
209
210 # Need one argument: path to layout
211 # If two arguments, then 2nd argument is the output file.
212
213 if len(arguments) > 0 and len(arguments) < 3:
214 layout_root = arguments[0]
215
216 if len(arguments) == 1:
217 out_filename = ""
218 elif len(arguments) > 2:
219 out_filename = arguments[1]
220
221 if len(arguments) > 0 and len(arguments) < 3:
222 run_full_drc(layout_root, out_filename)
223 else:
224 print("Usage: run_standard_drc.py <layout_name> [<output_file>] [options]")
225 print("Options:")
226 print(" (none)")
227
228