Made a number of changes to the foundry_install script to allow LEF annotation
to be used in conjunction with the "compile" or "compile-only" options,
indicating that the LEF file sources should only be used to annotate the cells
in magic, and that the LEF views generated from magic should then be used to
compile a LEF library. The Makefile was modified to use this method for the
sky130_fd_io library. Abstract views that had been staged in the custom area
as a workaround were removed.
diff --git a/common/foundry_install.py b/common/foundry_install.py
index 80e5c64..457483c 100755
--- a/common/foundry_install.py
+++ b/common/foundry_install.py
@@ -658,7 +658,8 @@
for option in optionlist[:]:
if option[0] == 'lef':
- have_lef = True
+ have_lefanno = True if 'annotate' in option or 'anno' in option else False
+ have_lef = True if not have_lefanno else False
if option[0] == 'techlef' or option[0] == 'techLEF':
have_techlef = True
elif option[0] == 'gds':
@@ -684,6 +685,9 @@
# Diagnostic
print("Install option: " + str(option[0]))
+ if option[0] == 'lef' and have_lefanno:
+ print("LEF files used for annotation only. Temporary install.")
+
# For ef_format: always make techlef -> techLEF and spice -> spi
if ef_format:
@@ -747,17 +751,6 @@
else:
print('Sorting files with script ' + sortscript)
- # 'anno' may be specified for LEF, in which case the LEF is used only
- # to annotate GDS and is not itself installed; this allows LEF to
- # be generated from Magic and avoids quirky use of obstruction layers.
- have_lefanno = True if 'annotate' in option or 'anno' in option else False
- if have_lefanno:
- if option[0] != 'lef':
- print("Warning: 'annotate' option specified outside of -lef. Ignoring.")
- else:
- # Mark as NOT having LEF since we want to use it only for annotation.
- have_lef = False
-
# For each library, create the library subdirectory
for library in libraries:
if len(library) == 3:
@@ -1058,6 +1051,8 @@
no_gds_convert = False
no_lef_convert = False
cdl_compile_only = False
+ lef_compile = False
+ lef_compile_only = False
cdl_exclude = []
lef_exclude = []
@@ -1105,10 +1100,18 @@
lef_reflib = '/libs.priv/'
# If CDL is marked 'compile-only' then CDL should only convert the
- # compiled file to SPICE if conversion is needed.
+ # compiled file to SPICE if conversion is needed. If LEF is marked
+ # 'compile' or 'compile-only' in annotate mode, then create a LEF
+ # library from magic LEF output.
+
if 'compile-only' in option:
if option[0] == 'cdl':
cdl_compile_only = True
+ elif option[0] == 'lef':
+ lef_compile_only = True
+ elif 'compile' in option:
+ if option[0] == 'lef':
+ lef_compile = True
# Find exclude list for any option
for item in option:
@@ -1137,7 +1140,6 @@
else:
tcllines = list(tclscript)
-
if have_gds and not no_gds_convert:
print("Migrating GDS files to layout.")
@@ -1288,10 +1290,7 @@
else:
# Use LEF files to set the port properties
if have_lefanno or have_lef:
- if have_lefanno:
- lefdirname = 'lefanno'
- else:
- lefdirname = 'lef'
+ lefdirname = 'lef'
# Find LEF file names in the source
if ef_format:
@@ -1303,7 +1302,20 @@
leffiles = os.listdir(lefsrclibdir)
leffiles = list(item for item in leffiles if os.path.splitext(item)[1] == '.lef')
- print('puts stdout "Annotating cells from LEF"', file=ofile)
+ if len(leffiles) > 0:
+ lefnames = list(os.path.split(item)[1] for item in leffiles)
+ notlefnames = []
+ for exclude in lef_exclude:
+ notlefnames.extend(fnmatch.filter(lefnames, exclude))
+
+ # Apply exclude list
+ if len(notlefnames) > 0:
+ for file in leffiles[:]:
+ if os.path.split(file)[1] in notlefnames:
+ leffiles.remove(file)
+
+ if len(leffiles) > 0:
+ print('puts stdout "Annotating cells from LEF"', file=ofile)
for leffile in leffiles:
print('lef read ' + lefsrclibdir + '/' + leffile, file=ofile)
@@ -1333,17 +1345,17 @@
if have_lefanno:
# Find LEF file names in the source
if ef_format:
- lefsrcdir = targetdir + lef_reflib + 'lefanno'
+ lefsrcdir = targetdir + lef_reflib + 'lef'
lefsrclibdir = lefsrcdir + '/' + destlib
else:
- lefsrcdir = targetdir + lef_reflib + destlib + '/lefanno'
+ lefsrcdir = targetdir + lef_reflib + destlib + '/lef'
lefsrclibdir = lefsrcdir
leffiles = os.listdir(lefsrclibdir)
leffiles = list(item for item in leffiles if os.path.splitext(item)[1] == '.lef')
# Get list of abstract views to make from LEF macros
for leffile in leffiles:
- with open(leffile, 'r') as ifile:
+ with open(lefsrclibdir + '/' + leffile, 'r') as ifile:
ltext = ifile.read()
llines = ltext.splitlines()
for lline in llines:
@@ -1467,13 +1479,18 @@
# write procedure, but we still need the pin use and class
# information from the LEF file, and maybe the bounding box.
+
+ # For annotation, the LEF file output will overwrite the
+ # original source LEF file.
+ lefdest = lefsrclibdir + '/' if have_lefanno else ''
+
for leffile in leffiles:
if have_lefanno:
print('lef read ' + lefsrclibdir + '/' + leffile, file=ofile)
for lefmacro in lefmacros:
print('if {[cellname list exists ' + lefmacro + '] != 0} {', file=ofile)
print(' load ' + lefmacro, file=ofile)
- print(' lef write ' + lefmacro + ' -hide', file=ofile)
+ print(' lef write ' + lefdest + lefmacro + ' -hide', file=ofile)
print('}', file=ofile)
print('puts stdout "Done."', file=ofile)
@@ -1498,63 +1515,9 @@
if mproc.returncode != 0:
print('ERROR: Magic exited with status ' + str(mproc.returncode))
- if not have_lef:
- print('No LEF file install; need to generate LEF.')
- # Remove the lefanno/ target and its contents.
- if have_lefanno:
- if ef_format:
- lefannosrcdir = targetdir + lef_reflib + 'lefanno'
- else:
- lefannosrcdir = targetdir + lef_reflib + destlib + '/lefanno'
- if os.path.isdir(lefannosrcdir):
- shutil.rmtree(lefannosrcdir)
-
- if ef_format:
- destlefdir = targetdir + lef_reflib + 'lef'
- destleflibdir = destlefdir + '/' + destlib
- else:
- destlefdir = targetdir + lef_reflib + destlib + '/lef'
- destleflibdir = destlefdir
-
- os.makedirs(destleflibdir, exist_ok=True)
- leflist = os.listdir(destlibdir)
- leflist = list(item for item in leflist if os.path.splitext(item)[1] == '.lef')
-
- # All macros will go into one file
- destleflib = destleflibdir + '/' + destlib + '.lef'
- # Remove any existing library file from the target directory
- if os.path.isfile(destleflib):
- print('Removing existing library ' + destleflib)
- os.remove(destleflib)
-
- first = True
- with open(destleflib, 'w') as ofile:
- for leffile in leflist:
- # Remove any existing single file from the target directory
- if os.path.isfile(destleflibdir + '/' + leffile):
- print('Removing ' + destleflibdir + '/' + leffile)
- os.remove(destleflibdir + '/' + leffile)
-
- # Append contents
- sourcelef = destlibdir + '/' + leffile
- with open(sourcelef, 'r') as ifile:
- leflines = ifile.read().splitlines()
- if not first:
- # Remove header from all but the first file
- leflines = leflines[8:]
- else:
- first = False
-
- for line in leflines:
- print(line, file=ofile)
-
- # Remove file from the source directory
- print('Removing source file ' + sourcelef)
- os.remove(sourcelef)
-
- # Set have_lef now that LEF files were made, so they
- # can be used to generate the maglef/ databases.
- have_lef = True
+ # Set have_lef now that LEF files were made, so they
+ # can be used to generate the maglef/ databases.
+ have_lef = True
elif not have_mag_8_2:
print('The installer is not able to run magic.')
@@ -1562,6 +1525,14 @@
print("Master PDK magic startup file not found. Did you install")
print("PDK tech files before PDK vendor files?")
+ if have_lefanno:
+ # LEF files were used for annotation. If "compile" or "compile-only"
+ # was also passed as an option, then build the LEF library now from
+ # the LEF output from magic.
+ print("Compiling LEF library from magic output.")
+ if lef_compile or lef_compile_only:
+ create_lef_library(lefsrclibdir, destlib, lef_compile_only, lef_exclude)
+
if have_lef and not no_lef_convert:
print("Migrating LEF files to layout.")
if ef_format: