Updated the klayout installation to take its contents from
the 3rd party repositories at Efabless and Mabrains,
avoiding the necessity of maintaining local versions of
the klayout setup and scripts for sky130.
diff --git a/Makefile.in b/Makefile.in
index 9adbc77..e63efb8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -34,6 +34,12 @@
 #
 #	remove the installed PDKs.
 #
+# make reference
+#
+#	annotate the technology JSON file with the current
+#	commit state of all repositories used by the PDK to
+#	create a reference set of PDKs.
+#
 #---------------------------------------------------
 #
 # The following definitions are tied to the contents
@@ -53,6 +59,7 @@
 TECHS_UNINSTALL = $(addprefix uninstall-,$(TECHS))
 TECHS_CLEAN = $(addprefix clean-,$(TECHS))
 TECHS_VERYCLEAN = $(addprefix veryclean-,$(TECHS))
+TECHS_REFERENCE = $(addprefix reference-,$(TECHS))
 
 #---------------------------------------------------
 
@@ -70,6 +77,9 @@
 update: $(TECHS_UPDATE)
 	@echo $(DONE_MESSAGE)
 
+reference: $(TECHS_REFERENCE)
+	@echo $(DONE_MESSAGE)
+
 clean: $(TECHS_CLEAN)
 	@echo $(DONE_MESSAGE)
 
@@ -130,6 +140,9 @@
 update-%: %
 	(cd $* && ${MAKE} update)
 
+reference-%: %
+	(cd $* && ${MAKE} reference)
+
 clean-%:
 	(cd $* && ${MAKE} clean)
 
diff --git a/VERSION b/VERSION
index e4cade8..7681bff 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.313
+1.0.314
diff --git a/common/save_commit_refs.py b/common/save_commit_refs.py
new file mode 100755
index 0000000..3987db1
--- /dev/null
+++ b/common/save_commit_refs.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python3
+#--------------------------------------------------------------------
+#
+# save_commit_refs.py
+#
+# This file is used to annotate a standard tech information JSON
+# file.  Each JSON file should have a section called "reference"
+# (if not, this script exits without doing anything).  The
+# "reference" section contains the commit hashes of repositories
+# used by the developer for generating the PDK, creating a
+# reference against which issues can be compared.  The "reference"
+# section is fixed and is not modified by replacement like the
+# rest of the JSON file.  It is the duty of the PDK developer to
+# update the references section periodically by running "make
+# distribution".
+#
+#--------------------------------------------------------------------
+# Usage:
+#
+#	save_commit_refs.py <filename> [-D<variable> ...]
+#
+# Where <filename> is the name of a technology JSON file, and
+# <variable> may be a <keyword>=<hash_value> pair only.
+# <keyword> corresponds to the variable name in a technology Makefile
+# for a commit hash that appears somewhere in <filename>.  <value>
+# is the commit hash value corresponding to <keyword>.
+#
+# The JSON file "reference" section is expected to have entries of
+# the form
+#	"<name>": "<commit_hash>"
+#
+# where <name> must also appear elsewhere in <filename> in an entry
+# of the form
+#	"<name>": "<keyword>"
+#
+# Then, where "-D<keyword>=<hash_value>" has been passed to the
+# script on the command line, the entry in the "reference" section
+# is changed by replacing "<commit_hash>" with "<hash_value>".
+#
+# The output of the script overwrites <filename> with the modified
+# contents.
+#
+# Example:
+#	sky130.json has an entry in "distribution":
+#		"magic": "fe2eb6d3906ed15ade0e7a51daea80dd4e3846e2"
+#	reflecting the git commit number of the program "magic" at
+#	the time the developer last ran save_commit_refs.py.
+#	Elsewhere in sky130.json, the following line appears:
+#		"magic": "MAGIC_COMMIT"
+#	If save_commit_refs.py is called as:
+#		save_commit_refs.py sky130.json -DMAGIC_COMMIT=abcdef
+#	then the line in "distribution" will be changed to:
+#		"magic": "abcdef"
+#
+#--------------------------------------------------------------------
+
+import os
+import re
+import sys
+
+def runsubs(keys, defines, inputfile, outputfile):
+
+    ifile = False
+    ifile = open(inputfile, 'r')
+    if not ifile:
+        print("Error:  Cannot open file " + inputfile + " for reading.\n", file=sys.stderr)
+        return
+
+    indist = False
+
+    filetext = ifile.readlines()
+    lastline = []
+
+    # Input file has been read, so close it.
+    ifile.close()
+
+    # Open output file
+    if not outputfile:
+        outputfile = inputfile
+
+    ofile = open(outputfile, 'w')
+    if not ofile:
+        print("Error:  Cannot open file " + outputfile + " for writing.\n", file=sys.stderr)
+        return
+
+    keyrex = re.compile('[ \t]*"([^"]+)":[ \t]*"[^"]+"')
+    valuerex = re.compile('[ \t]*"[^"]+":[ \t]*"([a-z0-9]+)"')
+    distdefs = {}
+    defs = []
+
+    for line in filetext:
+        newline = line
+
+        if indist == False:
+            if '"distribution":' in line:
+                indist = True
+            else:
+                # Find values matching keywords
+                for key in keys:
+                    if key in line:
+                        kmatch = keyrex.match(line)
+                        if kmatch:
+                            print('Found match "' + kmatch.group(1) + '" for key "' + key + '"')
+                            distdefs[kmatch.group(1)] = defines[key]
+                            defs.append(kmatch.group(1))
+        else:
+            # Find definitions matching keywords
+            for defx in defs:
+                if defx in line:
+                    vmatch = valuerex.match(line)
+                    if vmatch:
+                        value = distdefs[defx]
+                        newline = line.replace(vmatch.group(1), value)
+
+        print(newline, file=ofile, end='')
+
+    ofile.close()
+    return
+
+def printusage(progname):
+    print('Usage: ' + progname + ' filename [-options]')
+    print('   Options are:')
+    print('      -help         Print this help text.')
+    print('      -quiet        Stop without error if input file is not found.')
+    print('      -ccomm        Remove C comments in /* ... */ delimiters.')
+    print('      -D<def>       Define word <def> and set its value to 1.')
+    print('      -D<def>=<val> Define word <def> and set its value to <val>.')
+    print('      -I<dir>       Add <dir> to search path for input files.')
+    return
+
+if __name__ == '__main__':
+
+   # Parse command line for options and arguments
+    options = []
+    arguments = []
+    for item in sys.argv[1:]:
+        if item.find('-', 0) == 0:
+            options.append(item)
+        else:
+            arguments.append(item)
+
+    if len(arguments) > 0:
+        inputfile = arguments[0]
+        if len(arguments) > 1:
+            outputfile = arguments[1]
+        else:
+            outputfile = []
+    else:
+        printusage(sys.argv[0])
+        sys.exit(0)
+
+    defines = {}
+    keys = []
+    for item in options:
+        result = item.split('=')
+        if result[0] == '-help':
+            printusage(sys.argv[0])
+            sys.exit(0)
+        elif result[0][0:2] == '-D':
+            keyword = result[0][2:]
+            value = result[1]
+            defines[keyword] = value
+            keys.append(keyword)
+        else:
+            print('Bad option ' + item + ', options are -help, -D<def>\n')
+            sys.exit(1)
+
+    if not os.path.isfile(inputfile):
+        if not quiet:
+            print("Error:  No input file " + inputfile + " found.")
+        else:
+            sys.exit(0)
+
+    runsubs(keys, defines, inputfile, outputfile)
+
+    # Set mode of outputfile to be equal to that of inputfile (if not stdout)
+    if outputfile:
+        statinfo = os.stat(inputfile)
+        mode = statinfo.st_mode
+        os.chmod(outputfile, mode)
+
+    sys.exit(0)
diff --git a/common/staging_install.py b/common/staging_install.py
index 1a88dee..68686bf 100755
--- a/common/staging_install.py
+++ b/common/staging_install.py
@@ -443,7 +443,9 @@
     print('Changing local path references from ' + stagingdir + ' to ' + finaldir)
     print('Part 1:  Tools')
 
-    needcheck = ['ngspice']
+    # If there are any tool directories that should *not* be checked, then add
+    # them to the list below.
+    noneedcheck = []
     techdirs = ['/libs.tech/']
     if has_priv:
         techdirs.append('/libs.priv/')
@@ -479,7 +481,10 @@
         if thispdk != link_from:
             print('Replacing files with symbolic links to ' + link_from + ' where possible.')
             for techdir in techdirs:
-                for tool in needcheck:
+                tools = os.listdir(writedir + techdir)
+                for tool in tools:
+                    if tool in noneedcheck:
+                        continue
                     tooldir = writedir + techdir + tool
                     srctooldir = link_from + techdir + tool
                     if checkdir != '':
diff --git a/scripts/configure b/scripts/configure
index 29224d0..1f3c68e 100755
--- a/scripts/configure
+++ b/scripts/configure
@@ -594,6 +594,8 @@
 SKY130_OSU_T15_PATH
 SKY130_OSU_T12_PATH
 SKY130_SRAM_MACROS_PATH
+PRECHECK_SKY130_PATH
+KLAYOUT_SKY130_PATH
 XSCHEM_SKY130_PATH
 SKY130_ML_XX_HD_PATH
 PATCH
@@ -609,6 +611,9 @@
 SKY130_SOURCE_PATH
 SKY130_ENABLED_VARIANTS
 SKY130_LINK_TARGETS
+GF180MCU_SOURCE_PATH
+GF180MCU_ENABLED_VARIANTS
+GF180MCU_LINK_TARGETS
 SED
 pkgpyexecdir
 pyexecdir
@@ -638,6 +643,7 @@
 docdir
 oldincludedir
 includedir
+runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -660,6 +666,9 @@
 ac_subst_files=''
 ac_user_opts='
 enable_option_checking
+enable_gf180mcu_pdk
+with_gf180mcu_link_targets
+with_gf180mcu_variants
 enable_sky130_pdk
 with_sky130_link_targets
 with_sky130_variants
@@ -672,6 +681,8 @@
 enable_xschem
 enable_alpha_sky130
 enable_xschem_sky130
+enable_klayout_sky130
+enable_precheck_sky130
 enable_sram_sky130
 enable_osu_t12_sky130
 enable_osu_t15_sky130
@@ -720,6 +731,7 @@
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -972,6 +984,15 @@
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1109,7 +1130,7 @@
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir
+		libdir localedir mandir runstatedir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1262,6 +1283,7 @@
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -1291,8 +1313,8 @@
   --disable-option-checking  ignore unrecognized --enable/--with options
   --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
   --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
-  --enable-sky130-pdk=[/path/to/sky130/] --disable-sky130-pdk
-                          "location of the source files for the sky130 (pdks
+  --enable-gf180mcu-pdk=[/path/to/gf180mcu/] --disable-gf180mcu-pdk
+                          "location of the source files for the gf180mcu (pdks
                           with a pdk_url file can automatically download them
                           if the path is omitted)"
   --enable-klayout
@@ -1329,6 +1351,12 @@
   --enable-xschem-sky130[=path]
                           Install xschem_sky130. If path is omitted, the repository
                           will be downloaded. [default=enabled]
+  --enable-klayout-sky130[=path]
+                          Install klayout_sky130. If path is omitted, the repository
+                          will be downloaded. [default=enabled]
+  --enable-precheck-sky130[=path]
+                          Install precheck_sky130. If path is omitted, the repository
+                          will be downloaded. [default=enabled]
   --enable-sram-sky130[=path]
                           Install sky130_sram_macros. If path is omitted, the repository
                           will be downloaded. [default=disabled]
@@ -1345,10 +1373,10 @@
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
-  --with-sky130-link-targets=none|source
+  --with-gf180mcu-link-targets=none|source
                           "make symbolic links to existing files
                           [default=none]"
-  --with-sky130-variants=all|A|B|...
+  --with-gf180mcu-variants=all|A|B|...
                           "compile/install specified PDK variants only
                           [default=all]"
   --with-ef-style         Use efabless style file system structure
@@ -1834,7 +1862,7 @@
   $as_echo_n "(cached) " >&6
 else
 
-	for am_cv_pathless_PYTHON in python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7  python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0 none; do
+	for am_cv_pathless_PYTHON in python python2 python3  python3.9 python3.8 python3.7 python3.6 python3.5 python3.4 python3.3  python3.2 python3.1 python3.0  python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1  python2.0 none; do
 	  test "$am_cv_pathless_PYTHON" = none && break
 	  prog="import sys
 # split strings by '.' and convert to numeric.  Append some zeros
@@ -1915,7 +1943,7 @@
 if ${am_cv_python_version+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-  am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[:3])"`
+  am_cv_python_version=`$PYTHON -c "import sys; print('%u.%u' % sys.version_info[:2])"`
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_python_version" >&5
 $as_echo "$am_cv_python_version" >&6; }
@@ -2181,13 +2209,111 @@
 # check for the source and install paths for each PDK.
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: Found technology directories: sky130" >&5
-$as_echo "$as_me: Found technology directories: sky130" >&6;}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Found technology directories: gf180mcu sky130" >&5
+$as_echo "$as_me: Found technology directories: gf180mcu sky130" >&6;}
 
 
 
     # --enable-pdk-[pdk]=/path/to/pdk
 
+        echo "Checking technology gf180mcu..."
+
+
+        GF180MCU_ENABLED_VARIANTS="all"
+        GF180MCU_SOURCE_PATH=""
+        GF180MCU_LINK_TARGETS="none"
+        GF180MCU_AUTO="0"
+
+        # Check whether --enable-gf180mcu-gf180mcu was given.
+if test "${enable_gf180mcu_pdk+set}" = set; then :
+  enableval=$enable_gf180mcu_pdk;
+                if test "$enableval" == "yes" -o "$enableval" == "YES"; then
+                    export GF180MCU_SOURCE_PATH=../sources/gf180mcu-pdk
+            	    GF180MCU_AUTO="1"
+		    ENABLED_TECHS="$ENABLED_TECHS gf180mcu"
+                elif test "$enableval" == "no" -o "$enableval" == "NO"; then
+                    echo "Disabling gf180mcu..."
+                else
+                    GF180MCU_SOURCE_PATH=$enableval
+		    ENABLED_TECHS="$ENABLED_TECHS gf180mcu"
+                fi
+
+fi
+
+
+        if [ "$GF180MCU_SOURCE_PATH" != "" ]; then
+            GF180MCU_SOURCE_PATH=`realpath $GF180MCU_SOURCE_PATH`
+            GF180MCU_BASENAME=`basename $GF180MCU_SOURCE_PATH`
+	    if [ "$GF180MCU_BASENAME" = "libraries" ]; then
+		GF180MCU_SOURCE_PATH=`dirname $GF180MCU_SOURCE_PATH`
+	    fi
+            # Basic check that the PDK path exists, unless depending on Makefile
+	    # to download it automatically.
+            if [ "$GF180MCU_AUTO" = "0" ]; then
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Checking specified path for 'gf180mcu' at $GF180MCU_SOURCE_PATH" >&5
+$as_echo "$as_me: Checking specified path for 'gf180mcu' at $GF180MCU_SOURCE_PATH" >&6;}
+                as_ac_File=`$as_echo "ac_cv_file_$GF180MCU_SOURCE_PATH" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $GF180MCU_SOURCE_PATH" >&5
+$as_echo_n "checking for $GF180MCU_SOURCE_PATH... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  test "$cross_compiling" = yes &&
+  as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "$GF180MCU_SOURCE_PATH"; then
+  eval "$as_ac_File=yes"
+else
+  eval "$as_ac_File=no"
+fi
+fi
+eval ac_res=\$$as_ac_File
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: 'gf180mcu' source path found at $GF180MCU_SOURCE_PATH" >&5
+$as_echo "$as_me: 'gf180mcu' source path found at $GF180MCU_SOURCE_PATH" >&6;}
+
+else
+
+                    as_fn_error $? "Specified path for 'gf180mcu' at $GF180MCU_SOURCE_PATH not found" "$LINENO" 5
+
+fi
+
+	    else
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: PDK 'gf180mcu' will be downloaded automatically during make." >&5
+$as_echo "$as_me: PDK 'gf180mcu' will be downloaded automatically during make." >&6;}
+	    fi
+
+            # --with-pdk-link-targets=PDK_LINK_TARGETS
+
+# Check whether --with-gf180mcu-link-targets was given.
+if test "${with_gf180mcu_link_targets+set}" = set; then :
+  withval=$with_gf180mcu_link_targets; GF180MCU_LINK_TARGETS=$with_gf180mcu_link_targets
+
+fi
+
+
+            { $as_echo "$as_me:${as_lineno-$LINENO}: Link targets set to $GF180MCU_LINK_TARGETS" >&5
+$as_echo "$as_me: Link targets set to $GF180MCU_LINK_TARGETS" >&6;}
+
+            # --with-pdk-variants=PDK_ENABLED_VARIANTS
+
+# Check whether --with-gf180mcu-variants was given.
+if test "${with_gf180mcu_variants+set}" = set; then :
+  withval=$with_gf180mcu_variants; GF180MCU_ENABLED_VARIANTS=$with_gf180mcu_variants
+
+fi
+
+
+            { $as_echo "$as_me:${as_lineno-$LINENO}: Enabled variants set to $GF180MCU_ENABLED_VARIANTS" >&5
+$as_echo "$as_me: Enabled variants set to $GF180MCU_ENABLED_VARIANTS" >&6;}
+        fi
+
+
+
+
+
         echo "Checking technology sky130..."
 
 
@@ -2289,7 +2415,7 @@
 
 
 # Export the list of known technologies to the Makefile
-ALL_TECHS="sky130"
+ALL_TECHS="gf180mcu sky130"
 
 
 # Set variables for tool setups
@@ -2591,6 +2717,90 @@
 
 
 
+    # echo target targetvar flag location
+
+    KLAYOUT_SKY130_PATH=""
+
+    # Check whether --enable-klayout-sky130 was given.
+if test "${enable_klayout_sky130+set}" = set; then :
+  enableval=$enable_klayout_sky130;
+            if test "$enableval" == "yes" -o "$enableval" == "YES"; then
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Package 'klayout_sky130' will be installed automatically during make." >&5
+$as_echo "$as_me: Package 'klayout_sky130' will be installed automatically during make." >&6;}
+        	export KLAYOUT_SKY130_PATH=../sources/klayout_sky130
+            elif test "$enableval" == "no" -o "$enableval" == "NO"; then
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Disabling package 'klayout_sky130'" >&5
+$as_echo "$as_me: Disabling package 'klayout_sky130'" >&6;}
+    		export KLAYOUT_SKY130_PATH=""
+            else
+                KLAYOUT_SKY130_PATH=$enableval
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Enabling package 'klayout_sky130' at $KLAYOUT_SKY130_PATH" >&5
+$as_echo "$as_me: Enabling package 'klayout_sky130' at $KLAYOUT_SKY130_PATH" >&6;}
+            fi
+            KLAYOUT_SKY130_PATH=`realpath $KLAYOUT_SKY130_PATH`
+
+else
+
+            { $as_echo "$as_me:${as_lineno-$LINENO}: Package 'klayout_sky130' will be installed automatically during make." >&5
+$as_echo "$as_me: Package 'klayout_sky130' will be installed automatically during make." >&6;}
+	    KLAYOUT_SKY130_PATH=../sources/klayout_sky130
+
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+    # echo target targetvar flag location
+
+    PRECHECK_SKY130_PATH=""
+
+    # Check whether --enable-precheck-sky130 was given.
+if test "${enable_precheck_sky130+set}" = set; then :
+  enableval=$enable_precheck_sky130;
+            if test "$enableval" == "yes" -o "$enableval" == "YES"; then
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Package 'precheck_sky130' will be installed automatically during make." >&5
+$as_echo "$as_me: Package 'precheck_sky130' will be installed automatically during make." >&6;}
+        	export PRECHECK_SKY130_PATH=../sources/precheck_sky130
+            elif test "$enableval" == "no" -o "$enableval" == "NO"; then
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Disabling package 'precheck_sky130'" >&5
+$as_echo "$as_me: Disabling package 'precheck_sky130'" >&6;}
+    		export PRECHECK_SKY130_PATH=""
+            else
+                PRECHECK_SKY130_PATH=$enableval
+                { $as_echo "$as_me:${as_lineno-$LINENO}: Enabling package 'precheck_sky130' at $PRECHECK_SKY130_PATH" >&5
+$as_echo "$as_me: Enabling package 'precheck_sky130' at $PRECHECK_SKY130_PATH" >&6;}
+            fi
+            PRECHECK_SKY130_PATH=`realpath $PRECHECK_SKY130_PATH`
+
+else
+
+            { $as_echo "$as_me:${as_lineno-$LINENO}: Package 'precheck_sky130' will be installed automatically during make." >&5
+$as_echo "$as_me: Package 'precheck_sky130' will be installed automatically during make." >&6;}
+	    PRECHECK_SKY130_PATH=../sources/precheck_sky130
+
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+
 
     # echo target targetvar flag location
 
diff --git a/scripts/configure.ac b/scripts/configure.ac
index 940362d..5ad8ea4 100755
--- a/scripts/configure.ac
+++ b/scripts/configure.ac
@@ -226,6 +226,8 @@
 
 M4_GEN_INSTALLATION(sky130_ml_xx_hd, alpha-sky130, ../sources)
 M4_GEN_INSTALLATION(xschem_sky130, xschem-sky130, ../sources)
+M4_GEN_INSTALLATION(klayout_sky130, klayout-sky130, ../sources)
+M4_GEN_INSTALLATION(precheck_sky130, precheck-sky130, ../sources)
 
 M4_OPT_INSTALLATION(sky130_sram_macros, sram-sky130, ../sources)
 M4_OPT_INSTALLATION(sky130_osu_t12, osu-t12-sky130, ../sources)
diff --git a/sky130/Makefile.in b/sky130/Makefile.in
index 500e65c..6b6bebe 100644
--- a/sky130/Makefile.in
+++ b/sky130/Makefile.in
@@ -81,6 +81,13 @@
 #		specified, then the xschem setup will be cloned from the
 #		repository and installed.
 #
+#	--enable-klayout-sky130[=<path>]
+#		If enabled, install the Sky130 setup for the klayout layout
+#		editor.  If <path> is specified, then the klayout setup is
+#		expected to be found rooted at the given path.  If not
+#		specified, then the klayout setup will be cloned from the
+#		repository and installed.
+#
 # External (third-party) libraries and tool setups are the following (disabled
 # by default):
 #
@@ -221,6 +228,8 @@
 # Path to independent library sources (to be added to configuration options).
 ALPHA_PATH = @SKY130_ML_XX_HD_PATH@
 XSCHEM_PATH = @XSCHEM_SKY130_PATH@
+KLAYOUT_PATH = @KLAYOUT_SKY130_PATH@
+PRECHECK_PATH = @PRECHECK_SKY130_PATH@
 SRAM_PATH = @SKY130_SRAM_MACROS_PATH@
 OSU_T12_PATH = @SKY130_OSU_T12_PATH@
 OSU_T15_PATH = @SKY130_OSU_T15_PATH@
@@ -229,6 +238,8 @@
 PDK_URL = https://github.com/google/skywater-pdk
 ALPHA_URL = https://github.com/PaulSchulz/sky130_pschulz_xx_hd
 XSCHEM_URL = https://github.com/StefanSchippers/xschem_sky130
+KLAYOUT_URL = https://github.com/mabrains/sky130_klayout_pdk
+PRECHECK_URL = https://github.com/efabless/mpw_precheck
 SRAM_URL = https://github.com/efabless/sky130_sram_macros
 OSU_T12_URL = https://foss-eda-tools.googlesource.com/skywater-pdk/libs/sky130_osu_sc_t12
 OSU_T15_URL = https://foss-eda-tools.googlesource.com/skywater-pdk/libs/sky130_osu_sc_t15
@@ -562,10 +573,18 @@
 	SPIEXT = spice
 endif
 
+reference: ${TECH}.json
+	# Rewrite the ${TECH}.json file to change the commit values in
+	# "reference" to reflect the state of the system when  "make
+	# reference" was run.  This is then committed to the open_pdks
+	# repository to create a known reference configuration of all
+	# tools.
+	../common/save_commit_refs.py ${COMMIT_DEFS} ${TECH}.json
+
 all: $(foreach var, ${VARIANTS}, all-$(var))
 
 # Handle prerequisites (fetch and install the PDK and requested libraries)
-prerequisites: pdk-repo alpha-repo xschem-repo sram-repo osu-t12-repo osu-t15-repo osu-t18-repo
+prerequisites: pdk-repo alpha-repo xschem-repo klayout-repo precheck-repo sram-repo osu-t12-repo osu-t15-repo osu-t18-repo
 
 pdk-repo:
 	if test "x${SKYWATER_PATH}" != "x" ; then \
@@ -607,6 +626,26 @@
 		fi ; \
 	fi
 
+klayout-repo:
+	if test "x${KLAYOUT_PATH}" != "x" ; then \
+		if test -d "${KLAYOUT_PATH}" ; then \
+			echo "Using existing installation of klayout setup from ${KLAYOUT_PATH}" ; \
+		else \
+			echo "Downloading klayout setup from ${KLAYOUT_URL}" ; \
+			../scripts/download.sh ${KLAYOUT_URL} ${KLAYOUT_PATH} ; \
+		fi ; \
+	fi
+
+precheck-repo:
+	if test "x${PRECHECK_PATH}" != "x" ; then \
+		if test -d "${PRECHECK_PATH}" ; then \
+			echo "Using existing installation of klayout setup from ${PRECHECK_PATH}" ; \
+		else \
+			echo "Downloading klayout setup from ${PRECHECK_URL}" ; \
+			../scripts/download.sh ${PRECHECK_URL} ${PRECHECK_PATH} ; \
+		fi ; \
+	fi
+
 osu-t12-repo:
 	if test "x${OSU_T12_PATH}" != "x" ; then \
 		if test -d "${OSU_T12_PATH}" ; then \
@@ -638,7 +677,7 @@
 	fi
 
 # Update prerequisites
-update: update-pdk-repo update-alpha-repo update-xschem-repo update-sram-repo update-osu-t12-repo update-osu-t15-repo update-osu-t18-repo
+update: update-pdk-repo update-alpha-repo update-xschem-repo update-klayout-repo update-precheck-repo update-sram-repo update-osu-t12-repo update-osu-t15-repo update-osu-t18-repo
 
 update-pdk-repo:
 	if test "x${SKYWATER_PATH}" != "x" ; then \
@@ -664,6 +703,18 @@
 		../scripts/update.sh ${XSCHEM_PATH} ; \
 	fi
 
+update-klayout-repo:
+	if test "x${KLAYOUT_PATH}" != "x" ; then \
+		echo "Updating klayout setup from ${KLAYOUT_URL}" ; \
+		../scripts/update.sh ${KLAYOUT_PATH} ; \
+	fi
+
+update-precheck-repo:
+	if test "x${PRECHECK_PATH}" != "x" ; then \
+		echo "Updating klayout setup from ${PRECHECK_URL}" ; \
+		../scripts/update.sh ${PRECHECK_PATH} ; \
+	fi
+
 update-osu-t12-repo:
 	if test "x${OSU_T12_PATH}" != "x" ; then \
 		echo "Updating OSU standard T12 cell library from ${OSU_T12_URL}" ; \
@@ -838,20 +889,6 @@
 		${IRSIM_STAGING_$*}/${SKY130$*}_$$corner.prm ; \
 	done
 
-klayout-%: klayout/${TECH}.lyp klayout/${TECH}.lyt
-	mkdir -p ${KLAYOUTTOP_STAGING_$*}
-	mkdir -p ${KLAYOUT_STAGING_$*}
-	rm -f ${KLAYOUT_STAGING_$*}/${SKY130$*}.lyp
-	rm -f ${KLAYOUT_STAGING_$*}/${SKY130$*}.lyt
-	${CPP} -utf8 ${SKY130$*_DEFS} klayout/${TECH}.lyp \
-		${KLAYOUT_STAGING_$*}/${SKY130$*}.lyp
-	${CPP} -utf8 ${SKY130$*_DEFS} klayout/${TECH}.lyt \
-		${KLAYOUT_STAGING_$*}/${SKY130$*}.lyt
-	${CPP} -utf8 ${SKY130$*_DEFS} klayout/${TECH}.lydrc \
-		${KLAYOUT_STAGING_$*}/${SKY130$*}.lydrc
-	#./custom/scripts/gen_run_drc.py -l ${KLAYOUT_STAGING_$*}/${SKY130$*}.lydrc \
-	#	-o ${KLAYOUT_STAGING_$*}/${SKY130$*}.drc
-
 xcircuit-%: xcircuit/${TECH}.xcircuitrc
 	rm -rf ${XCIRCUIT_STAGING_$*}
 	mkdir -p ${XCIRCUITTOP_STAGING_$*}
@@ -866,6 +903,31 @@
 	${CPP} ${SKY130$*_DEFS} xcircuit/${TECH}.xcircuitrc \
 		${XCIRCUIT_STAGING_$*}/${SKY130$*}.xcircuitrc
 
+klayout-%: ${KLAYOUT_PATH}
+	if test "x${KLAYOUT_PATH}" != "x" ; then \
+		rm -rf ${KLAYOUT_STAGING_$*} ; \
+		mkdir -p ${KLAYOUT_STAGING_$*} ; \
+		mkdir -p ${KLAYOUT_STAGING_$*}/tech ; \
+		mkdir -p ${KLAYOUT_STAGING_$*}/drc ; \
+		mkdir -p ${KLAYOUT_STAGING_$*}/lvs ; \
+		mkdir -p ${KLAYOUT_STAGING_$*}/scripts ; \
+		mkdir -p ${KLAYOUT_STAGING_$*}/pymacros ; \
+	fi
+	# Copy lvs and pymacro directories from the repository.  Other files
+	# are rearranged to the preferred structure.
+	if test "x${KLAYOUT_PATH}" != "x" ; then \
+		cp -rp ${KLAYOUT_PATH}/sky130_tech/tech/sky130/lvs/* ${KLAYOUT_STAGING_$*}/lvs/ ; \
+		cp -rp ${KLAYOUT_PATH}/sky130_tech/tech/sky130/pymacros/* ${KLAYOUT_STAGING_$*}/pymacros/ ; \
+		cp ${KLAYOUT_PATH}/sky130_tech/tech/sky130/${TECH}.lyp ${KLAYOUT_STAGING_$*}/tech/${SKY130$*}.lyp ; \
+		cp ${KLAYOUT_PATH}/sky130_tech/tech/sky130/${TECH}.lyt ${KLAYOUT_STAGING_$*}/tech/${SKY130$*}.lyt ; \
+		cp -rp ${KLAYOUT_PATH}/scripts/* ${KLAYOUT_STAGING_$*}/scripts/ ; \
+	fi
+	# Copy drc directory contents from the Efabless Open MPW precheck repository
+	if test "x${PRECHECK_PATH}" != "x" ; then \
+		cp ${PRECHECK_PATH}/checks/tech-files/sky130A_mr.drc ${KLAYOUT_STAGING_$*}/drc/${SKY130$*}_mr.drc ; \
+		cp -rp ${PRECHECK_PATH}/checks/drc_checks/klayout/* ${KLAYOUT_STAGING_$*}/drc/ ; \
+	fi
+
 xschem-%: ${XSCHEM_PATH}
 	if test "x${XSCHEM_PATH}" != "x" ; then \
 		rm -rf ${XSCHEM_STAGING_$*} ; \
@@ -1089,6 +1151,65 @@
 	fi
 
 primitive-%:
+	# Install device models from custom files
+	${STAGE} -source ./custom -target ${STAGING_PATH}/${SKY130$*} \
+		-ngspice sky130_fd_pr/combined_models/* \
+		2>&1 | tee -a ${SKY130$*}_make.log
+	# Install device layouts from custom sources
+	${STAGE} -source ./custom -target ${STAGING_PATH}/${SKY130$*} \
+		-gds sky130_fd_pr/*.gds \
+			options=custom/scripts/gds_import_setup.tcl \
+		-library primitive sky130_fd_pr 2>&1 | tee -a ${SKY130$*}_make.log
+	# Install base device library from vendor files.
+	${STAGE} -source ${SKYWATER_LIBS_PATH} -target ${STAGING_PATH}/${SKY130$*} \
+		-gds %l/latest/cells/*/*.gds \
+			no-copy=custom/sky130_fd_pr/*.gds \
+			include=custom/sky130_fd_pr/*.gds \
+			compile-only \
+			options=custom/scripts/gds_import_setup.tcl \
+		-lef %l/latest/cells/*/*.magic.lef compile-only \
+		-spice %l/latest/cells/*/*.spice filter=custom/scripts/rename_cells.py \
+		-library primitive sky130_fd_pr 2>&1 | tee -a ${SKY130$*}_make.log
+
+	# Custom:  Patch the models to remove the substrate pin from the PNP
+	# device, which has no independent substrate pin (collector=substrate)
+	${PATCH} -p4 -f -d ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE} \
+		< custom/patches/sky130_fd_pr_3.patch \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+
+	# Fix up the PNP model file before running the next modification
+	head -15 ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__pnp_05v5_W3p40L3p40.model.spice > ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/temp
+	tail -39 ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__pnp_05v5_W3p40L3p40.model.spice >> ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/temp
+	mv ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/temp  ${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__pnp_05v5_W3p40L3p40.model.spice
+	# Custom:  Parse the (commented out) statistics blocks and generate
+	# ngspice-compatible monte carlo parameters for mismatch and process
+	./custom/scripts/mismatch_params.py ${EF_FORMAT} -variant=${SKY130$*} \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+	./custom/scripts/process_params.py ${EF_FORMAT} -variant=${SKY130$*} \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+	# Custom:  Change vt to local_vt in one file for Xyce compatibilty
+	./custom/scripts/xyce_hack.py \
+		${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__res_iso_pw.model.spice \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+	# Custom:  Remove ACM model parameters from BSIM3 devices
+	./custom/scripts/xyce_hack2.py \
+		${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__special_nfet_pass.pm3.spice \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+	./custom/scripts/xyce_hack2.py \
+		${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__special_pfet_pass.pm3.spice \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+	./custom/scripts/xyce_hack2.py \
+		${STAGING_PATH}/${SKY130$*}/libs.ref/${PR_SPICE}/sky130_fd_pr__special_nfet_latch.pm3.spice \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+
+	# Custom:  Add special device ID layers to bipolar layouts in magic
+	# to make the extraction models correct.
+	./custom/scripts/add_bipolar_ids.py ${EF_FORMAT} -variant=${SKY130$*} \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
+
+primitive-legacy-%:
+	# This is the former recipe for primitive-% and has been superceded
+	# by the combined device models found in custom/sky130_fd_pr/.
 	# Install device subcircuits from vendor files
 	${STAGE} -source ${SKYWATER_LIBS_PATH} -target ${STAGING_PATH}/${SKY130$*} \
 		-ngspice sky130_fd_pr/latest/models/* \
@@ -1106,7 +1227,6 @@
 			include=custom/sky130_fd_pr/*.gds \
 			compile-only \
 			options=custom/scripts/gds_import_setup.tcl \
-		-cdl %l/latest/cells/*/*.cdl compile-only \
 		-lef %l/latest/cells/*/*.magic.lef compile-only \
 		-spice %l/latest/cells/*/*.spice filter=custom/scripts/rename_cells.py \
 		-library primitive sky130_fd_pr 2>&1 | tee -a ${SKY130$*}_make.log
@@ -1234,6 +1354,10 @@
 	# when writing HVI to GDS during hierarchical adjustments.
 	${ADDPROP} ${STAGING_PATH}/${SKY130$*} sky130_fd_io sky130_fd_io__top_gpiov2 \
 		"MASKHINTS_HVI 1346 17198 5828 19224 13700 1890 15920 2360 24 17522 1778 20612" -mag
+	# Run the add_properties.py script to add extraction information to layout
+	# cells for which the intended device cannot be extracted.
+	./custom/scripts/add_properties.py ${EF_FORMAT} -variant=${SKY130$*} \
+		2>&1 | tee -a ${SKY130$*}_make.log || true
 
 digital-hd-%:
 	# Install custom additions to standard cell libraries
diff --git a/sky130/custom/scripts/add_properties.py b/sky130/custom/scripts/add_properties.py
new file mode 100755
index 0000000..556ab74
--- /dev/null
+++ b/sky130/custom/scripts/add_properties.py
@@ -0,0 +1,346 @@
+#!/usr/bin/env python3
+#
+#--------------------------------------------------------------------
+# Add a \"device\" property to the layout of specific PDK primitive
+# devices that do not extract as intended.  This includes, for
+# example, RF devices (which extract as non-RF devices), vertical
+# parallel plate capacitors (which extract as metal wires), and any
+# other device that is not represented in magic's \"extract\" section.
+#
+# This uses the method introduced in magic version 8.3.298 using
+# property "device" with value "primitive", so that the subcell is
+# output to the netlist without a corresponding subcircuit entry or
+# black box entry, as the device is assumed to be defined in the PDK.
+# But it is necessary to make sure that the ports are in the correct
+# order for the PDK device.
+#--------------------------------------------------------------------
+
+import os
+import re
+import sys
+import subprocess
+
+options = []
+arguments = []
+for item in sys.argv[1:]:
+    if item.find('-', 0) == 0:
+        options.append(item[1:])
+    else:
+        arguments.append(item)
+
+ef_format = False
+variant = 'sky130A'
+magpath = variant + '/libs.ref/sky130_fd_pr/mag'
+maglefpath = variant + '/libs.ref/sky130_fd_pr/maglef'
+spicepath = variant + '/libs.ref/sky130_fd_pr/spice'
+
+if len(options) > 0:
+    for option in options:
+        if option.startswith('variant'):
+            variant = option.split('=')[1]
+    magpath = variant + '/libs.ref/sky130_fd_pr/mag'
+    maglefpath = variant + '/libs.ref/sky130_fd_pr/maglef'
+    spicepath = variant + '/libs.ref/sky130_fd_pr/spice'
+    for option in options:
+        if option == 'ef_format':
+            ef_format = True
+            magpath = variant + '/libs.ref/mag/sky130_fd_pr'
+            maglefpath = variant + '/libs.ref/maglef/sky130_fd_pr'
+            spicepath = variant + '/libs.ref/spice/sky130_fd_pr'
+elif len(arguments) > 0:
+    magpath = arguments[0]
+    maglefpath = magpath.replace('/mag/', '/maglef/')
+    spicepath = magpath.replace('/mag/', '/spice/')
+
+# \"devlist\" is an enumeration of all devices that have both a layout
+# in libs.ref/sky130_fd_pr/mag and corresponding subcircuit models
+# in libs.ref/sky130_fd_pr/spice, and for which extracting the layout
+# in magic does not produce the intended SPICE model.
+
+devlist = ['sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap',
+	   'sky130_fd_pr__cap_vpp_02p7x11p1_m1m2m3m4_shieldl1_fingercap',
+	   'sky130_fd_pr__cap_vpp_02p7x21p1_m1m2m3m4_shieldl1_fingercap',
+	   'sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap',
+	   'sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2',
+	   'sky130_fd_pr__cap_vpp_03p9x03p9_m1m2_shieldl1_floatm3',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_l1m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_l1m1m2_shieldpo_floatm3',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_shieldl1',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4',
+	   'sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4',
+	   'sky130_fd_pr__cap_vpp_05p9x05p9_m1m2m3m4_shieldl1_wafflecap',
+	   'sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4',
+	   'sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3_shieldl1m4',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_l1m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_l1m1m2_shieldpo_floatm3',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_shieldl1',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4',
+	   'sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4',
+	   'sky130_fd_pr__cap_vpp_11p3x11p3_m1m2m3m4_shieldl1_wafflecap',
+	   'sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhv',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2_shieldpom3',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldm4',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldpom4',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5_x',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5_x',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_shieldl1',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldm5',
+	   'sky130_fd_pr__cap_vpp_11p5x11p7_m1m4_noshield',
+	   'sky130_fd_pr__cap_vpp_44p7x23p1_pol1m1m2m3m4m5_noshield',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W1p65L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W3p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W3p00L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W3p00L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W5p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W5p00L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_bM02W5p00L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W1p65L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W1p65L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W1p65L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W3p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W3p00L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W3p00L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_bM04W5p00L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF02W0p42L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF02W0p84L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF02W3p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF04W0p84L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF04W3p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF08W0p84L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_aF08W3p00L0p15',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_bM02W5p00L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_bM02W5p00L0p25',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p18',
+	   'sky130_fd_pr__rf_nfet_01v8_lvt_bM04W5p00L0p25',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM02W3p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM02W5p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W3p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W5p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM04W7p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM10W3p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM10W5p00L0p50',
+	   'sky130_fd_pr__rf_nfet_g5v0d10v5_bM10W7p00L0p50',
+	   'sky130_fd_pr__rf_pfet_01v8_aF02W0p84L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_aF02W1p68L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_aF02W3p00L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_aF02W5p00L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_aF04W1p68L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W1p65L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W1p65L0p18',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W1p65L0p25',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W3p00L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W3p00L0p18',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W3p00L0p25',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W5p00L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W5p00L0p18',
+	   'sky130_fd_pr__rf_pfet_01v8_bM02W5p00L0p25',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W1p65L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W1p65L0p18',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W1p65L0p25',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W3p00L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W3p00L0p18',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W3p00L0p25',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W5p00L0p15',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W5p00L0p18',
+	   'sky130_fd_pr__rf_pfet_01v8_bM04W5p00L0p25',
+	   'sky130_fd_pr__rf_pfet_01v8_mvt_aF02W0p84L0p15']
+
+
+# The portlist array contains the names of the ports used in the layout in the order that
+# they appear in the device subcircuit model.  Note that none of the pin names match between
+# the layout and device definition, so the usual annotation method in magic won't work.
+
+portlist = [	['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET3'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET3'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET4'],
+		['C0', 'C1', 'SUB', 'MET4'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET3'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET3'],
+		['C0', 'C1', 'SUB', 'MET4'],
+		['C0', 'C1', 'SUB', 'MET4'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB', 'MET5'],
+		['C0', 'C1', 'SUB'],
+		['C0', 'C1', 'SUB'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'SUBSTRATE'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK'],
+		['DRAIN', 'GATE', 'SOURCE', 'BULK']]
+
+#--------------------------------------------------------------------
+# Do this for files both in mag/ and maglef/ . . .
+
+pathlist = [magpath, maglefpath]
+
+for idx, device in enumerate(devlist):
+    for path in pathlist:
+        infile_name = path + '/' + device + '.mag'
+
+        if not os.path.exists(infile_name):
+            print('Error:  Cannot find file ' + infile_name)
+            continue
+
+        print('Adding special extraction property to device ' + device + ' layout')
+
+        propcmd = ['../common/insert_property.py', variant, 'sky130_fd_pr',
+		device, 'device primitive']
+        if ef_format:
+            propcmd.append('-ef_format')
+
+        # Just call the insert_property.py script
+        subprocess.run(propcmd, stdout = subprocess.DEVNULL, stderr = subprocess.DEVNULL)
+
+        # Now renumber the ports in the layout to match the PDK device.  The port names in
+        # the layout generally don't match the port names in the PDK device definitions,
+        # so ports are handled by manually listing them all out.
+
+        devports = portlist[idx]
+        modified = False
+
+        with open(infile_name, 'r') as ifile:
+            magdata = ifile.read()
+            outlines = []
+            for line in magdata.splitlines():
+                if line.startswith('port'):
+                    if lname and lname in devports:
+                        tokens = line.split()
+                        newindex = str(1 + devports.index(lname))
+                        if newindex != tokens[1]:
+                            modified = True
+                        tokens[1] = newindex
+                        newline = ' '.join(tokens)
+                    else:
+                        newline = line
+                    lname = None
+                elif line.startswith('flabel'):
+                    lname = line.split()[-1]
+                    newline = line
+                elif line.startswith('rlabel'):
+                    lname = line.split()[-1]
+                    newline = line
+                else:
+                    lname = None
+                    newline = line
+                outlines.append(newline)
+
+        if modified:
+            print('Modifying port order in ' + infile_name)
+            with open(infile_name, 'w') as ofile:
+                ofile.write('\n'.join(outlines))
+
+    # Any SPICE netlist for this device that came from extraction in magic during the
+    # PDK build is invalid, and should be removed.
+
+    infile_name = spicepath + '/' + device + '.spice'
+    if os.path.isfile(infile_name):
+        os.remove(infile_name)
+
diff --git a/sky130/klayout/README b/sky130/klayout/README
new file mode 100644
index 0000000..fac454a
--- /dev/null
+++ b/sky130/klayout/README
@@ -0,0 +1,4 @@
+The files in this directory are deprecated and have been
+superceded by files that are maintained by third party
+sources (Efabless and Mabrains) and are pulled in by
+cloning the relevant repositories.
diff --git a/sky130/klayout/sky130A_mr.drc b/sky130/klayout/sky130A_mr.drc
deleted file mode 100755
index ca2965e..0000000
--- a/sky130/klayout/sky130A_mr.drc
+++ /dev/null
@@ -1,752 +0,0 @@
-#  DRC  for  SKY130 according to :
-#   https://skywater-pdk.readthedocs.io/en/latest/rules/periphery.html
-#   https://skywater-pdk.readthedocs.io/en/latest/rules/layers.html
-#
-#   Distributed under GNU GPLv3: https://www.gnu.org/licenses/
-#
-#  History :
-#   2022-1-13 : 2022.1.13_01.03 release
-#
-##########################################################################################
-
-# optionnal for a batch launch :   klayout -b -rd input=my_layout.gds -rd report=sky130_drc.txt -r drc_sky130.drc
-if $input
-  source($input, $top_cell)
-end
-
-if $report
-  report("SKY130 DRC runset", $report)
-else
-  report("SKY130 DRC runset", File.join(File.dirname(RBA::CellView::active.filename), "sky130_drc.txt"))
-end
-
-AL = true  # do not change
-CU = false  # do not change
-# choose betwen only one of  AL  or  CU  back-end flow here :
-backend_flow = AL
-
-# enable / disable rule groups
-if $feol  == "1"        || $feol == "true"
-  FEOL         = true # front-end-of-line checks
-else
-  FEOL         = false
-end
-
-if $beol == "1"         || $beol == "true"
-  BEOL         = true # back-end-of-line checks
-else
-  BEOL         = false
-end
-
-if $offgrid == "1"      || $offgrid == "true"
-  OFFGRID      = true # manufacturing grid/angle checks
-else
-  OFFGRID      = false
-end
-
-if $seal  == "1"        || $seal == "true"
-  SEAL         = true # SEAL RING checks
-else
-  SEAL         = false
-end
-
-if $floating_met == "1" || $floating_met == "true"
-  FLOATING_MET = true # back-end-of-line checks
-else
-  FLOATING_MET = false
-end
-
-# klayout setup
-########################
-# use a tile size of 1mm - not used in deep mode-
-# tiles(1000.um)
-# use a tile border of 10 micron:
-# tile_borders(1.um)
-#no_borders
-
-# hierachical
-deep
-
-if $thr
-    threads($thr)
-else
-    threads(4)
-end
-
-# if more inof is needed, set true
-# verbose(true)
-verbose(true)
-
-# layers definitions
-########################
-
-# all except purpose (datatype) 5 -- label and 44 -- via
-li_wildcard = "67/20"
-mcon_wildcard = "67/44"
-
-m1_wildcard = "68/20"
-via_wildcard = "68/44"
-
-m2_wildcard = "69/20"
-via2_wildcard = "69/44"
-
-m3_wildcard = "70/20"
-via3_wildcard = "70/44"
-
-m4_wildcard = "71/20"
-via4_wildcard = "71/44"
-
-m5_wildcard = "72/20"
-
-diff = input(65, 20)
-tap = polygons(65, 44)
-nwell = polygons(64, 20)
-dnwell = polygons(64, 18)
-pwbm = polygons(19, 44)
-pwde = polygons(124, 20)
-natfet = polygons(124, 21)
-hvtr = polygons(18, 20)
-hvtp = polygons(78, 44)
-ldntm = polygons(11, 44)
-hvi = polygons(75, 20)
-tunm = polygons(80, 20)
-lvtn = polygons(125, 44)
-poly = polygons(66, 20)
-hvntm = polygons(125, 20)
-nsdm = polygons(93, 44)
-psdm = polygons(94, 20)
-rpm = polygons(86, 20)
-urpm = polygons(79, 20)
-npc = polygons(95, 20)
-licon = polygons(66, 44)
-
-li = polygons(li_wildcard)
-mcon = polygons(mcon_wildcard)
-
-m1 = polygons(m1_wildcard)
-via = polygons(via_wildcard)
-
-m2 = polygons(m2_wildcard)
-via2 = polygons(via2_wildcard)
-
-m3 = polygons(m3_wildcard)
-via3 = polygons(via3_wildcard)
-
-m4 = polygons(m4_wildcard)
-via4 = polygons(via4_wildcard)
-
-m5 = polygons(m5_wildcard)
-
-pad = polygons(76, 20)
-nsm = polygons(61, 20)
-capm = polygons(89, 44)
-cap2m = polygons(97, 44)
-vhvi = polygons(74, 21)
-uhvi = polygons(74, 22)
-npn = polygons(82, 20)
-inductor = polygons(82, 24)
-vpp = polygons(82, 64)
-pnp = polygons(82, 44)
-lvs_prune = polygons(84, 44)
-ncm = polygons(92, 44)
-padcenter = polygons(81, 20)
-mf = polygons(76, 44)
-areaid_sl = polygons(81, 1)
-areaid_ce = polygons(81, 2)
-areaid_fe = polygons(81, 3)
-areaid_sc = polygons(81, 4)
-areaid_sf = polygons(81, 6)
-areaid_sw = polygons(81, 7)
-areaid_sr = polygons(81, 8)
-areaid_mt = polygons(81, 10)
-areaid_dt = polygons(81, 11)
-areaid_ft = polygons(81, 12)
-areaid_ww = polygons(81, 13)
-areaid_ld = polygons(81, 14)
-areaid_ns = polygons(81, 15)
-areaid_ij = polygons(81, 17)
-areaid_zr = polygons(81, 18)
-areaid_ed = polygons(81, 19)
-areaid_de = polygons(81, 23)
-areaid_rd = polygons(81, 24)
-areaid_dn = polygons(81, 50)
-areaid_cr = polygons(81, 51)
-areaid_cd = polygons(81, 52)
-areaid_st = polygons(81, 53)
-areaid_op = polygons(81, 54)
-areaid_en = polygons(81, 57)
-areaid_en20 = polygons(81, 58)
-areaid_le = polygons(81, 60)
-areaid_hl = polygons(81, 63)
-areaid_sd = polygons(81, 70)
-areaid_po = polygons(81, 81)
-areaid_it = polygons(81, 84)
-areaid_et = polygons(81, 101)
-areaid_lvt = polygons(81, 108)
-areaid_re = polygons(81, 125)
-areaid_ag = polygons(81, 79)
-poly_rs = polygons(66, 13)
-diff_rs = polygons(65, 13)
-pwell_rs = polygons(64, 13)
-li_rs = polygons(67, 13)
-cfom = polygons(22, 20)
-
-
-# Define a new custom function that selects polygons by their number of holes:
-# It will return a new layer containing those polygons with min to max holes.
-# max can be nil to omit the upper limit.
-class DRC::DRCLayer
-  def with_holes(min, max)
-    new_data = RBA::Region::new
-    self.data.each do |p|
-      if p.holes >= (min || 0) && (!max || p.holes <= max)
-        new_data.insert(p)
-      end
-    end
-    DRC::DRCLayer::new(@engine, new_data)
-  end
-end
-
-# DRC section
-########################
-log("DRC section")
-
-if FEOL
-log("FEOL section")
-#   dnwell
-log("START: 64/18 (dnwell)")
-dnwell.width(3.0, euclidian).output("dnwell.2", "dnwell.2 : min. dnwell width : 3.0um")
-log("END: 64/18 (dnwell)")
-
-#   nwell
-log("START: 64/20 (nwell)")
-nwell.width(0.84, euclidian).output("nwell.1", "nwell.1 : min. nwell width : 0.84um")
-nwell.space(1.27, euclidian).output("nwell.2a", "nwell.2a : min. nwell spacing (merged if less) : 1.27um")
-nwell_interact = nwell.merge
-dnwell.enclosing(nwell_interact.holes, 1.03, euclidian).output("nwell.6", "nwell.6 : min enclosure of nwellHole by dnwell : 1.03um")
-log("END: 64/20 (nwell)")
-
-#   hvtp
-log("START: 78/44 (hvtp)")
-hvtp.width(0.38, euclidian).output("hvtp.1", "hvtp.1 : min. hvtp width : 0.38um")
-hvtp.space(0.38, euclidian).output("hvtp.2", "hvtp.2 : min. hvtp spacing : 0.38um")
-log("END: 78/44 (hvtp)")
-
-#   hvtr
-log("START: 18/20 (htvr)")
-hvtr.width(0.38, euclidian).output("hvtr.1", "hvtr.1 : min. hvtr width : 0.38um")
-hvtr.separation(hvtp, 0.38, euclidian).output("hvtr.2", "hvtr.2 : min. hvtr spacing : 0.38um")
-hvtr.and(hvtp).output("hvtr.2_a", "hvtr.2_a : hvtr must not overlap hvtp")
-log("END: 18/20 (htvr)")
-
-#   lvtn
-log("START: 25/44 (lvtn)")
-lvtn.width(0.38, euclidian).output("lvtn.1a", "lvtn.1a : min. lvtn width : 0.38um")
-lvtn.space(0.38, euclidian).output("lvtn.2", "lvtn.2 : min. lvtn spacing : 0.38um")
-log("END: 25/44 (lvtn)")
-
-#   ncm
-log("START: 92/44 (ncm)")
-ncm.width(0.38, euclidian).output("ncm.1", "ncm.1 : min. ncm width : 0.38um")
-ncm.space(0.38, euclidian).output("ncm.2a", "ncm.2a : min. ncm spacing : 0.38um")
-log("END: 92/44 (ncm)")
-
-#   diff-tap
-log("START: 65/20 (diff)")
-difftap = diff.or(tap)
-diff_width = diff.rectangles.width(0.15, euclidian).polygons
-diff_cross_areaid_ce = diff_width.edges.outside_part(areaid_ce).not(diff_width.outside(areaid_ce).edges)
-diff_cross_areaid_ce.output("difftap.1", "difftap.1 : min. diff width across areaid:ce : 0.15um")
-diff.outside(areaid_ce).width(0.15, euclidian).output("difftap.1_a", "difftap.1_a : min. diff width in periphery : 0.15um")
-log("END: 65/20 (diff)")
-
-log("START: 65/44 (tap)")
-tap_width = tap.rectangles.width(0.15, euclidian).polygons
-tap_cross_areaid_ce = tap_width.edges.outside_part(areaid_ce).not(tap_width.outside(areaid_ce).edges)
-tap_cross_areaid_ce.output("difftap.1_b", "difftap.1_b : min. tap width across areaid:ce : 0.15um")
-tap.not(areaid_ce).width(0.15, euclidian).output("difftap.1_c", "difftap.1_c : min. tap width in periphery : 0.15um")
-log("END: 65/44 (tap)")
-
-difftap.space(0.27, euclidian).output("difftap.3", "difftap.3 : min. difftap spacing : 0.27um")
-
-#   tunm
-log("START: 80/20 (tunm)")
-tunm.width(0.41, euclidian).output("tunm.1", "tunm.1 : min. tunm width : 0.41um")
-tunm.space(0.5, euclidian).output("tunm.2", "tunm.2 : min. tunm spacing : 0.5um")
-log("END: 80/20 (tunm)")
-
-#   poly
-log("START: 66/20 (poly)")
-poly.width(0.15, euclidian).output("poly.1a", "poly.1a : min. poly width : 0.15um")
-poly.not(areaid_ce).space(0.21, euclidian).output("poly.2", "poly.2 : min. poly spacing : 0.21um")
-
-
-#   rpm
-log("START: 86/20 (rpm)")
-rpm.width(1.27, euclidian).output("rpm.1a", "rpm.1a : min. rpm width : 1.27um")
-rpm.space(0.84, euclidian).output("rpm.2", "rpm.2 : min. rpm spacing : 0.84um")
-log("END: 86/20 (rpm)")
-
-#   urpm
-log("START: 79/20 (urpm)")
-urpm.width(1.27, euclidian).output("urpm.1a", "urpm.1a : min. rpm width : 1.27um")
-urpm.space(0.84, euclidian).output("urpm.2", "urpm.2 : min. rpm spacing : 0.84um")
-log("END: 79/20 (urpm)")
-
-#   npc
-log("START: 95/20 (npc)")
-npc.width(0.27, euclidian).output("npc.1", "npc.1 : min. npc width : 0.27um")
-npc.space(0.27, euclidian).output("npc.2", "npc.2 : min. npc spacing, should be manually merged if less than : 0.27um")
-log("END: 95/20 (npc)")
-
-#   nsdm
-log("START: 93/44 (nsdm)")
-nsdm.outside(areaid_ce).width(0.38, euclidian).output("nsd.1", "nsd.1 : min. nsdm width : 0.38um")
-nsdm.not(areaid_ce).space(0.38, euclidian).output("nsd.2", "nsd.2 : min. nsdm spacing, should be manually merged if less than : 0.38um")
-log("END: 93/44 (nsdm)")
-
-#   psdm
-log("START: 94/20 (psdm)")
-psdm.outside(areaid_ce).width(0.38, euclidian).output("psd.1", "psd.1 : min. psdm width : 0.38um")
-psdm.not(areaid_ce).space(0.38, euclidian).output("psd.2", "psd.2 : min. psdm spacing, should be manually merged if less than : 0.38um")
-log("END: 94/20 (psdm)")
-
-#   licon
-log("START: 66/44 (licon)")
-if SEAL
-  ringLICON = licon.drc(with_holes > 0)
-  rectLICON = licon.not(ringLICON)
-else
-  rectLICON = licon
-end
-xfom = difftap.not(poly)
-licon1ToXfom = licon.interacting(licon.and(xfom))
-licon1ToXfom_PERI = licon1ToXfom.not(areaid_ce)
-rectLICON.non_rectangles.output("licon.1", "licon.1 : licon should be rectangle")
-rectLICON.not(rpm.or(urpm)).edges.without_length(0.17).output("licon.1_a/b", "licon.1_a/b : minimum/maximum width of licon : 0.17um")
-licon1ToXfom_PERI.separation(npc, 0.09, euclidian).output("licon.13", "licon.13 : min. difftap licon spacing to npc : 0.09um")
-licon1ToXfom_PERI.and(npc).output("licon.13_a", "licon.13_a : licon of diffTap in periphery must not overlap npc")
-licon.interacting(poly).and(licon.interacting(difftap)).output("licon.17", "licon.17 : Licons may not overlap both poly and (diff or tap)")
-log("END: 66/44 (licon)")
-
-# CAPM
-log("START: 89/44 (capm)")
-m3_bot_plate = (capm.and(m3)).sized(0.14)
-capm.width(1.0, euclidian).output("capm.1", "capm.1 : min. capm width : 1.0um")
-capm.space(0.84, euclidian).output("capm.2a", "capm.2a : min. capm spacing : 0.84um")
-m3.interacting(capm).isolated(1.2, euclidian).output("capm.2b", "capm.2b : min. capm spacing : 1.2um")
-m3_bot_plate.isolated(1.2, euclidian).output("capm.2b_a", "capm.2b_a : min. spacing of m3_bot_plate : 1.2um")
-capm.and(m3).enclosing(m3, 0.14, euclidian).output("capm.3", "capm.3 : min. capm and m3 enclosure of m3 : 0.14um")
-m3.enclosing(capm, 0.14, euclidian).output("capm.3_a", "capm.3_a : min. m3 enclosure of capm : 0.14um")
-capm.enclosing(via3, 0.14, euclidian).output("capm.4", "capm.4 : min. capm enclosure of via3 : 0.14um")
-capm.separation(via3, 0.14, euclidian).output("capm.5", "capm.5 : min. capm spacing to via3 : 0.14um")
-log("END: 89/44 (capm)")
-
-# CAP2M
-log("START: 97/44 (cap2m)")
-m4_bot_plate = (cap2m.and(m4)).sized(0.14)
-cap2m.width(1.0, euclidian).output("cap2m.1", "cap2m.1 : min. cap2m width : 1.0um")
-cap2m.space(0.84, euclidian).output("cap2m.2a", "cap2m.2a : min. cap2m spacing : 0.84um")
-m4.interacting(cap2m).isolated(1.2, euclidian).output("cap2m.2b", "cap2m.2b : min. cap2m spacing : 1.2um")
-# This rule has false positive errors
-m4_bot_plate.isolated(1.2, euclidian).output("cap2m.2b_a", "cap2m.2b_a : min. spacing of m4_bot_plate : 1.2um")
-cap2m.and(m4).enclosing(m4, 0.14, euclidian).output("cap2m.3", "cap2m.3 : min. m4 enclosure of cap2m : 0.14um")
-m4.enclosing(cap2m, 0.14, euclidian).output("cap2m.3_a", "cap2m.3_a : min. m4 enclosure of cap2m : 0.14um")
-cap2m.enclosing(via4, 0.2, euclidian).output("cap2m.4", "cap2m.4 : min. cap2m enclosure of via4 : 0.14um")
-cap2m.separation(via4, 0.2, euclidian).output("cap2m.5", "cap2m.5 : min. cap2m spacing to via4 : 0.14um")
-log("END: 97/44 (cap2m)")
-end #FEOL
-
-if BEOL
-log("BEOL section")
-
-#   li
-log("START: 67/20 (li)")
-linotace = li.not(areaid_ce)
-linotace.width(0.17, euclidian).output("li.1", "li.1 : min. li width : 0.17um")
-# This rule is taking a long time in some slots
-linotace.edges.space(0.17, euclidian).output("li.3", "li.3 : min. li spacing : 0.17um")
-licon_peri = licon.not(areaid_ce)
-li_edges_with_less_enclosure = li.enclosing(licon_peri, 0.08, projection).second_edges
-error_corners = li_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
-li_interact = licon_peri.interacting(error_corners.polygons(1.dbu))
-li_interact.output("li.5", "li.5 : min. li enclosure of licon of 2 adjacent edges : 0.08um")
-li.with_area(nil, 0.0561).output("li.6", "li.6 : min. li area : 0.0561um²")
-log("END: 67/20 (li)")
-
-#   ct
-log("START: 67/44 (mcon)")
-mconnotace = mcon.not(areaid_ce)
-if SEAL
-  ringMCON = mcon.drc(with_holes > 0)
-  rectMCON = mcon.not(ringMCON)
-else
-  rectMCON = mcon
-end
-rectMCON_peri = rectMCON.not(areaid_ce)
-rectMCON.non_rectangles.output("ct.1", "ct.1: non-ring mcon should be rectangular")
-# rectMCON_peri.edges.without_length(0.17).output("ct.1_a/b", "ct.1_a/b : minimum/maximum width of mcon : 0.17um")
-rectMCON_peri.drc(width < 0.17).output("ct.1_a", "ct.1_a : minimum width of mcon : 0.17um")
-rectMCON_peri.drc(length > 0.17).output("ct.1_b", "ct.1_b : maximum length of mcon : 0.17um")
-mcon.space(0.19, euclidian).output("ct.2", "ct.2 : min. mcon spacing : 0.19um")
-if SEAL
-  ringMCON.width(0.17, euclidian).output("ct.3", "ct.3 : min. width of ring-shaped mcon : 0.17um")
-  ringMCON.drc(width >= 0.175).output("ct.3_a", "ct.3_a : max. width of ring-shaped mcon : 0.175um")
-  ringMCON.not(areaid_sl).output("ct.3_b", "ct.3_b: ring-shaped mcon must be enclosed by areaid_sl")
-end
-mconnotace.not(li).output("ct.4", "ct.4 : mcon should covered by li")
-log("END: 67/44 (mcon)")
-
-#   m1
-log("START: 68/20 (m1)")
-m1.width(0.14, euclidian).output("m1.1", "m1.1 : min. m1 width : 0.14um")
-huge_m1 = m1.sized(-1.5).sized(1.5).snap(0.005) & m1
-non_huge_m1 = m1.edges - huge_m1
-huge_m1 = huge_m1.edges.outside_part(m1.merged)
-
-non_huge_m1.space(0.14, euclidian).output("m1.2", "m1.2 : min. m1 spacing : 0.14um")
-
-(huge_m1.separation(non_huge_m1, 0.28, euclidian) + huge_m1.space(0.28, euclidian)).output("m1.3ab", "m1.3ab : min. 3um.m1 spacing m1 : 0.28um")
-
-#not_in_cell6 = layout(source.cell_obj).select("-s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b", "-s8fpls_pl8", "-s8fs_cmux4_fm")
-not_in_cell6 = layout(source.cell_obj).select("-s8cell_ee_plus_sseln_a", "-s8cell_ee_plus_sseln_b", "-s8cell_ee_plus_sselp_a", "-s8cell_ee_plus_sselp_b", "-s8fs_cmux4_fm")
-not_in_cell6_m1 = not_in_cell6.input(m1_wildcard)
-
-not_in_cell6_m1.enclosing(mconnotace, 0.03, euclidian).output("791_m1.4", "791_m1.4 : min. m1 enclosure of mcon : 0.03um")
-mconnotace.not(m1).output("m1.4", "m1.4 : mcon periphery must be enclosed by m1")
-in_cell6 = layout(source.cell_obj).select("-*", "+s8cell_ee_plus_sseln_a", "+s8cell_ee_plus_sseln_b", "+s8cell_ee_plus_sselp_a", "+s8cell_ee_plus_sselp_b", "+s8fpls_pl8", "+s8fs_cmux4_fm")
-in_cell6_m1 = in_cell6.input(m1_wildcard)
-in_cell6_m1.enclosing(mcon, 0.005, euclidian).output("m1.4a", "m1.4a : min. m1 enclosure of mcon for specific cells : 0.005um")
-
-in_cell6_m1.not(m1).output('m1.4a_a', 'm1.4a_a : mcon periph must be enclosed by met1 for specific cells')
-
-m1.with_area(0..0.083).output("m1.6", "m1.6 : min. m1 area : 0.083um²")
-
-m1.holes.with_area(0..0.14).output("m1.7", "m1.7 : min. m1 with holes area : 0.14um²")
-if FLOATING_MET
-  m1.not_interacting(via.or(mcon)).output("m1.x", "floating met1, must interact with via1")
-end
-
-if backend_flow = AL
-  #Could flag false positive, fix would be to add .rectangles for m1
-  mconnotace_edges_with_less_enclosure_m1 = m1.enclosing(mconnotace, 0.06, projection).second_edges
-  error_corners_m1 = mconnotace_edges_with_less_enclosure_m1.width(angle_limit(100.0), 1.dbu)
-  mconnotace_interact_m1 = mconnotace.interacting(error_corners_m1.polygons(1.dbu))
-  mconnotace_interact_m1.output("m1.5", "m1.5 : min. m1 enclosure of mcon of 2 adjacent edges : 0.06um")
-end
-log("END: 68/20 (m1)")
-
-#   via
-log("START: 68/44 (via)")
-if backend_flow = AL
-  if SEAL
-    ringVIA = via.drc(with_holes > 0)
-    rectVIA = via.not(ringVIA)
-  else
-    rectVIA = via
-  end
-
-  via_not_mt = rectVIA.not(areaid_mt)
-
-  via_not_mt.non_rectangles.output("via.1a", "via.1a : via outside of moduleCut should be rectangular")
-  via_not_mt.width(0.15, euclidian).output("via.1a_a", "via.1a_a : min. width of via outside of moduleCut : 0.15um")
-  # via_not_mt.edges.without_length(nil, 0.15 + 1.dbu).output("via.1a_b", "via.1a_b : maximum length of via : 0.15um")
-  via_not_mt.drc(length > 0.15).output("via.1a_b", "via.1a_b : maximum length of via : 0.15um")
-
-  via.space(0.17, euclidian).output("via.2", "via.2 : min. via spacing : 0.17um")
-
-  if SEAL
-    ringVIA.width(0.2, euclidian).output("via.3", "via.3 : min. width of ring-shaped via : 0.2um")
-    ringVIA.drc(width >= 0.205).output("via.3_a", "via.3_a : max. width of ring-shaped via : 0.205um")
-    ringVIA.not(areaid_sl).output("via.3_b", "via.3_b: ring-shaped via must be enclosed by areaid_sl")
-  end
-
-  m1.edges.enclosing(rectVIA.drc(width == 0.15), 0.055, euclidian).output("via.4a", "via.4a : min. m1 enclosure of 0.15um via : 0.055um")
-  rectVIA.squares.drc(width == 0.15).not(m1).output("via.4a_a", "via.4a_a : 0.15um via must be enclosed by met1")
-
-  via1_edges_with_less_enclosure_m1 = m1.edges.enclosing(rectVIA.drc(width == 0.15), 0.085, projection).second_edges
-  error_corners_via1 = via1_edges_with_less_enclosure_m1.width(angle_limit(100.0), 1.dbu)
-  via2_interact = via.interacting(error_corners_via1.polygons(1.dbu))
-  via2_interact.output("via.5a", "via.5a : min. m1 enclosure of 0.15um via of 2 adjacent edges : 0.085um")
-
-end
-log("END: 68/44 (via)")
-
-#   m2
-log("START: 69/20 (m2)")
-m2.width(0.14, euclidian).output("m2.1", "m2.1 : min. m2 width : 0.14um")
-
-huge_m2 = m2.sized(-1.5).sized(1.5).snap(0.005) & m2
-non_huge_m2 = m2.edges - huge_m2
-huge_m2 = huge_m2.edges.outside_part(m2.merged)
-via_outside_periphery = via.not(areaid_ce)
-
-non_huge_m2.space(0.14, euclidian).output("m2.2", "m2.2 : min. m2 spacing : 0.14um")
-
-(huge_m2.separation(non_huge_m2, 0.28, euclidian) + huge_m2.space(0.28, euclidian)).output("m2.3ab", "m2.3ab : min. 3um.m2 spacing m2 : 0.28um")
-
-m2.with_area(0..0.0676).output("m2.6", "m2.6 : min. m2 area : 0.0676um²")
-m2.holes.with_area(0..0.14).output("m2.7", "m2.7 : min. m2 holes area : 0.14um²")
-if FLOATING_MET
-  m2.not_interacting(via.or(via2)).output("m2.x", "floating met2, must interact with via1 or via2")
-end
-if backend_flow = AL
-  m2.enclosing(via_outside_periphery, 0.055, euclidian).output("m2.4", "m2.4 : min. m2 enclosure of via : 0.055um")
-  via_outside_periphery.not(m2).output("m2.4_a", "m2.4_a : via in periphery must be enclosed by met2")
-  via_edges_with_less_enclosure_m2 = m2.enclosing(via, 0.085, projection).second_edges
-  error_corners = via_edges_with_less_enclosure_m2.width(angle_limit(100.0), 1.dbu)
-  via_interact = via.interacting(error_corners.polygons(1.dbu))
-  via_interact.output("m2.5", "m2.5 : min. m2 enclosure of via of 2 adjacent edges : 0.085um")
-
-end
-log("END: 69/20 (m2)")
-
-#   via2
-log("START: 69/44 (via2)")
-if backend_flow = AL
-  if SEAL
-    ringVIA2 = via2.drc(with_holes > 0)
-    rectVIA2 = via2.not(ringVIA2)
-  else
-    rectVIA2 = via2
-  end
-
-  via2_not_mt = rectVIA2.not(areaid_mt)
-  via2_not_mt.non_rectangles.output("via2.1a", "via2.1a : via2 outside of moduleCut should be rectangular")
-  via2_not_mt.width(0.2, euclidian).output("via2.1a_a", "via2.1a_a : min. width of via2 outside of moduleCut : 0.2um")
-  via2_not_mt.edges.without_length(nil, 0.2 + 1.dbu).output("via2.1a_b", "via2.1a_b : maximum length of via2 : 0.2um")
-  via2.space(0.2, euclidian).output("via2.2", "via2.2 : min. via2 spacing : 0.2um")
-
-  if SEAL
-    ringVIA2.width(0.2, euclidian).output("via2.3", "via2.3 : min. width of ring-shaped via2 : 0.2um")
-    ringVIA2.drc(width >= 0.205).output("via2.3_a", "via2.3_a : max. width of ring-shaped via2 : 0.205um")
-    ringVIA2.not(areaid_sl).output("via2.3_b", "via2.3_b: ring-shaped via2 must be enclosed by areaid_sl")
-  end
-
-  m2.enclosing(via2, 0.04, euclidian).output("via2.4", "via2.4 : min. m2 enclosure of via2 : 0.04um")
-  via2.not(m2).output("via2.4_a", "via2.4_a : via must be enclosed by met2")
-
-  via2_edges_with_less_enclosure = m2.enclosing(via2, 0.085, projection).second_edges
-  error_corners = via2_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
-  via2_interact = via2.interacting(error_corners.polygons(1.dbu))
-  via2_interact.output("via2.5", "via2.5 : min. m3 enclosure of via2 of 2 adjacent edges : 0.085um")
-end
-log("END: 69/44 (via2)")
-
-#   m3
-log("START: 70/20 (m3)")
-m3.width(0.3, euclidian).output("m3.1", "m3.1 : min. m3 width : 0.3um")
-
-huge_m3 = m3.sized(-1.5).sized(1.5).snap(0.005) & m3
-non_huge_m3 = m3.edges - huge_m3
-huge_m3 = huge_m3.edges.outside_part(m3.merged)
-
-non_huge_m3.space(0.3, euclidian).output("m3.2", "m3.2 : min. m3 spacing : 0.3um")
-
-(huge_m3.separation(non_huge_m3, 0.4, euclidian) + huge_m3.space(0.4, euclidian)).output("m3.3cd", "m3.3cd : min. 3um.m3 spacing m3 : 0.4um")
-if FLOATING_MET
-  m3.not_interacting(via2.or(via3)).output("m3.x", "floating met3, must interact with via2 or via3")
-end
-if backend_flow = AL
-  m3.enclosing(via2, 0.065, euclidian).output("m3.4", "m3.4 : min. m3 enclosure of via2 : 0.065um")
-  via2.not(m3).output("m3.4_a", "m3.4_a : via2 must be enclosed by met3")
-end
-log("END: 70/20 (m3)")
-
-#   via3
-log("START: 70/44 (via3)")
-if backend_flow = AL
-  if SEAL
-    ringVIA3 = via3.drc(with_holes > 0)
-    rectVIA3 = via3.not(ringVIA3)
-  else
-    rectVIA3 = via3
-  end
-
-  via3_not_mt = rectVIA3.not(areaid_mt)
-  via3_not_mt.non_rectangles.output("via3.1", "via3.1 : via3 outside of moduleCut should be rectangular")
-  via3_not_mt.width(0.2, euclidian).output("via3.1_a", "via3.1_a : min. width of via3 outside of moduleCut : 0.2um")
-  via3_not_mt.edges.without_length(nil, 0.2 + 1.dbu).output("via3.1_b", "via3.1_b : maximum length of via3 : 0.2um")
-
-  via3.space(0.2, euclidian).output("via3.2", "via3.2 : min. via3 spacing : 0.2um")
-  m3.enclosing(via3, 0.06, euclidian).output("via3.4", "via3.4 : min. m3 enclosure of via3 : 0.06um")
-  rectVIA3.not(m3).output("via3.4_a", "via3.4_a : non-ring via3 must be enclosed by met3")
-
-  via_edges_with_less_enclosure = m3.enclosing(via3, 0.09, projection).second_edges
-  error_corners = via_edges_with_less_enclosure.width(angle_limit(100.0), 1.dbu)
-  via3_interact = via3.interacting(error_corners.polygons(1.dbu))
-  via3_interact.output("via3.5", "via3.5 : min. m3 enclosure of via3 of 2 adjacent edges : 0.09um")
-end
-log("END: 70/44 (via3)")
-
-#   m4
-log("START: 71/20 (m4)")
-m4.width(0.3, euclidian).output("m4.1", "m4.1 : min. m4 width : 0.3um")
-
-huge_m4 = m4.sized(-1.5).sized(1.5).snap(0.005) & m4
-non_huge_m4 = m4.edges - huge_m4
-huge_m4 = huge_m4.edges.outside_part(m4.merged)
-
-non_huge_m4.space(0.3, euclidian).output("m4.2", "m4.2 : min. m4 spacing : 0.3um")
-
-m4.with_area(0..0.240).output("m4.4a", "m4.4a : min. m4 area : 0.240um²")
-
-(huge_m4.separation(non_huge_m4, 0.4, euclidian) + huge_m4.space(0.4, euclidian)).output("m4.5ab", "m4.5ab : min. 3um.m4 spacing m4 : 0.4um")
-if FLOATING_MET
-  m4.not_interacting(via3.or(via4)).output("m4.x", "floating met3, must interact with via3 or via4")
-end
-if backend_flow = AL
-  m4.enclosing(via3, 0.065, euclidian).output("m4.3", "m4.3 : min. m4 enclosure of via3 : 0.065um")
-  via3.not(m4).output("m4.3_a", "m4.3_a : via3 must be enclosed by met4")
-end
-log("END: 71/20 (m4)")
-
-#   via4
-log("START: 71/44 (via4)")
-if SEAL
-  ringVIA4 = via4.drc(with_holes > 0)
-  rectVIA4 = via4.not(ringVIA4)
-else
-  rectVIA4 = via4
-end
-
-via4_not_mt = rectVIA4.not(areaid_mt)
-via4_not_mt.non_rectangles.output("via4.1", "via4.1 : via4 outside of moduleCut should be rectangular")
-rectVIA4.width(0.8, euclidian).output("via4.1_a", "via4.1_a : min. width of via4 outside of moduleCut : 0.8um")
-rectVIA4.drc(length > 0.8).output("via4.1_b", "via4.1_b : maximum length of via4 : 0.8um")
-
-via4.space(0.8, euclidian).polygons.output("via4.2", "via4.2 : min. via4 spacing : 0.8um")
-
-if SEAL
-  ringVIA4.width(0.8, euclidian).output("via4.3", "via4.3 : min. width of ring-shaped via4 : 0.8um")
-  ringVIA4.drc(width >= 0.805).output("via4.3_a", "via4.3_a : max. width of ring-shaped via4 : 0.805um")
-  ringVIA4.not(areaid_sl).output("via4.3_b", "via4.3_b: ring-shaped via4 must be enclosed by areaid_sl")
-end
-
-m4.enclosing(via4, 0.19, euclidian).output("via4.4", "via4.4 : min. m4 enclosure of via4 : 0.19um")
-rectVIA4.not(m4).output("via4.4_a", "via4.4_a : m4 must enclose all via4")
-log("END: 71/44 (via4)")
-
-#   m5
-log("START: 72/20 (m5)")
-m5.width(1.6, euclidian).output("m5.1", "m5.1 : min. m5 width : 1.6um")
-
-m5.space(1.6, euclidian).output("m5.2", "m5.2 : min. m5 spacing : 1.6um")
-
-m5.enclosing(via4, 0.31, euclidian).output("m5.3", "m5.3 : min. m5 enclosure of via4 : 0.31um")
-via4.not(m5).output("m5.3_a", "m5.3_a : via must be enclosed by m5")
-if FLOATING_MET
-  m5.not_interacting(via4).output("m5.x", "floating met5, must interact with via4")
-end
-m5.with_area(0..4.0).output("m5.4", "m5.4 : min. m5 area : 4.0um²")
-log("END: 72/20 (m5)")
-
-#   pad
-log("START: 76/20 (pad)")
-pad.space(1.27, euclidian).output("pad.2", "pad.2 : min. pad spacing : 1.27um")
-log("END: 76/20 (pad)")
-
-end #BEOL
-
-if FEOL
-log("FEOL section")
-
-#   hvi
-log("START: 75/20 (hvi)")
-hvi_peri = hvi.not(areaid_ce)
-hvi_peri.width(0.6, euclidian).output("hvi.1", "hvi.1 : min. hvi width : 0.6um")
-hvi_peri.space(0.7, euclidian).output("hvi.2a", "hvi.2a : min. hvi spacing : 0.7um")
-log("END: 75/20 (hvi)")
-
-#   hvntm
-log("START: 125/20 (hvntm)")
-hvntm_peri = hvntm.not(areaid_ce)
-hvntm_peri.width(0.7, euclidian).output("hvntm.1", "hvntm.1 : min. hvntm width : 0.7um")
-hvntm_peri.space(0.7, euclidian).output("hvntm.2", "hvntm.2 : min. hvntm spacing : 0.7um")
-log("END: 125/20 (hvntm)")
-
-end #FEOL
-
-
-if OFFGRID
-log("OFFGRID-ANGLES section")
-
-dnwell.ongrid(0.005).output("dnwell_OFFGRID", "x.1b : OFFGRID vertex on dnwell")
-dnwell.with_angle(0 .. 45).output("dnwell_angle", "x.3a : non 45 degree angle dnwell")
-nwell.ongrid(0.005).output("nwell_OFFGRID", "x.1b : OFFGRID vertex on nwell")
-nwell.with_angle(0 .. 45).output("nwell_angle", "x.3a : non 45 degree angle nwell")
-pwbm.ongrid(0.005).output("pwbm_OFFGRID", "x.1b : OFFGRID vertex on pwbm")
-pwbm.with_angle(0 .. 45).output("pwbm_angle", "x.3a : non 45 degree angle pwbm")
-pwde.ongrid(0.005).output("pwde_OFFGRID", "x.1b : OFFGRID vertex on pwde")
-pwde.with_angle(0 .. 45).output("pwde_angle", "x.3a : non 45 degree angle pwde")
-hvtp.ongrid(0.005).output("hvtp_OFFGRID", "x.1b : OFFGRID vertex on hvtp")
-hvtp.with_angle(0 .. 45).output("hvtp_angle", "x.3a : non 45 degree angle hvtp")
-hvtr.ongrid(0.005).output("hvtr_OFFGRID", "x.1b : OFFGRID vertex on hvtr")
-hvtr.with_angle(0 .. 45).output("hvtr_angle", "x.3a : non 45 degree angle hvtr")
-lvtn.ongrid(0.005).output("lvtn_OFFGRID", "x.1b : OFFGRID vertex on lvtn")
-lvtn.with_angle(0 .. 45).output("lvtn_angle", "x.3a : non 45 degree angle lvtn")
-ncm.ongrid(0.005).output("ncm_OFFGRID", "x.1b : OFFGRID vertex on ncm")
-ncm.with_angle(0 .. 45).output("ncm_angle", "x.3a : non 45 degree angle ncm")
-diff.ongrid(0.005).output("diff_OFFGRID", "x.1b : OFFGRID vertex on diff")
-tap.ongrid(0.005).output("tap_OFFGRID", "x.1b : OFFGRID vertex on tap")
-diff.not(areaid_en.and(uhvi)).with_angle(0 .. 90).output("diff_angle", "x.2 : non 90 degree angle diff")
-diff.and(areaid_en.and(uhvi)).with_angle(0 .. 45).output("diff_angle", "x.2c : non 45 degree angle diff")
-tap.not(areaid_en.and(uhvi)).with_angle(0 .. 90).output("tap_angle", "x.2 : non 90 degree angle tap")
-tap.and(areaid_en.and(uhvi)).with_angle(0 .. 45).output("tap_angle", "x.2c : non 45 degree angle tap")
-tunm.ongrid(0.005).output("tunm_OFFGRID", "x.1b : OFFGRID vertex on tunm")
-tunm.with_angle(0 .. 45).output("tunm_angle", "x.3a : non 45 degree angle tunm")
-poly.ongrid(0.005).output("poly_OFFGRID", "x.1b : OFFGRID vertex on poly")
-poly.with_angle(0 .. 90).output("poly_angle", "x.2 : non 90 degree angle poly")
-rpm.ongrid(0.005).output("rpm_OFFGRID", "x.1b : OFFGRID vertex on rpm")
-rpm.with_angle(0 .. 45).output("rpm_angle", "x.3a : non 45 degree angle rpm")
-npc.ongrid(0.005).output("npc_OFFGRID", "x.1b : OFFGRID vertex on npc")
-npc.with_angle(0 .. 45).output("npc_angle", "x.3a : non 45 degree angle npc")
-nsdm.ongrid(0.005).output("nsdm_OFFGRID", "x.1b : OFFGRID vertex on nsdm")
-nsdm.with_angle(0 .. 45).output("nsdm_angle", "x.3a : non 45 degree angle nsdm")
-psdm.ongrid(0.005).output("psdm_OFFGRID", "x.1b : OFFGRID vertex on psdm")
-psdm.with_angle(0 .. 45).output("psdm_angle", "x.3a : non 45 degree angle psdm")
-licon.ongrid(0.005).output("licon_OFFGRID", "x.1b : OFFGRID vertex on licon")
-licon.with_angle(0 .. 90).output("licon_angle", "x.2 : non 90 degree angle licon")
-li.ongrid(0.005).output("li_OFFGRID", "x.1b : OFFGRID vertex on li")
-li.with_angle(0 .. 45).output("li_angle", "x.3a : non 45 degree angle li")
-mcon.ongrid(0.005).output("ct_OFFGRID", "x.1b : OFFGRID vertex on mcon")
-mcon.with_angle(0 .. 90).output("ct_angle", "x.2 : non 90 degree angle mcon")
-vpp.ongrid(0.005).output("vpp_OFFGRID", "x.1b : OFFGRID vertex on vpp")
-vpp.with_angle(0 .. 45).output("vpp_angle", "x.3a : non 45 degree angle vpp")
-m1.ongrid(0.005).output("m1_OFFGRID", "x.1b : OFFGRID vertex on m1")
-m1.with_angle(0 .. 45).output("m1_angle", "x.3a : non 45 degree angle m1")
-via.ongrid(0.005).output("via_OFFGRID", "x.1b : OFFGRID vertex on via")
-via.with_angle(0 .. 90).output("via_angle", "x.2 : non 90 degree angle via")
-m2.ongrid(0.005).output("m2_OFFGRID", "x.1b : OFFGRID vertex on m2")
-m2.with_angle(0 .. 45).output("m2_angle", "x.3a : non 45 degree angle m2")
-via2.ongrid(0.005).output("via2_OFFGRID", "x.1b : OFFGRID vertex on via2")
-via2.with_angle(0 .. 90).output("via2_angle", "x.2 : non 90 degree angle via2")
-m3.ongrid(0.005).output("m3_OFFGRID", "x.1b : OFFGRID vertex on m3")
-m3.with_angle(0 .. 45).output("m3_angle", "x.3a : non 45 degree angle m3")
-via3.ongrid(0.005).output("via3_OFFGRID", "x.1b : OFFGRID vertex on via3")
-via3.with_angle(0 .. 90).output("via3_angle", "x.2 : non 90 degree angle via3")
-nsm.ongrid(0.005).output("nsm_OFFGRID", "x.1b : OFFGRID vertex on nsm")
-nsm.with_angle(0 .. 45).output("nsm_angle", "x.3a : non 45 degree angle nsm")
-m4.ongrid(0.005).output("m4_OFFGRID", "x.1b : OFFGRID vertex on m4")
-m4.with_angle(0 .. 45).output("m4_angle", "x.3a : non 45 degree angle m4")
-via4.ongrid(0.005).output("via4_OFFGRID", "x.1b : OFFGRID vertex on via4")
-via4.with_angle(0 .. 90).output("via4_angle", "x.2 : non 90 degree angle via4")
-m5.ongrid(0.005).output("m5_OFFGRID", "x.1b : OFFGRID vertex on m5")
-m5.with_angle(0 .. 45).output("m5_angle", "x.3a : non 45 degree angle m5")
-pad.ongrid(0.005).output("pad_OFFGRID", "x.1b : OFFGRID vertex on pad")
-pad.with_angle(0 .. 45).output("pad_angle", "x.3a : non 45 degree angle pad")
-mf.ongrid(0.005).output("mf_OFFGRID", "x.1b : OFFGRID vertex on mf")
-mf.with_angle(0 .. 90).output("mf_angle", "x.2 : non 90 degree angle mf")
-hvi.ongrid(0.005).output("hvi_OFFGRID", "x.1b : OFFGRID vertex on hvi")
-hvi.with_angle(0 .. 45).output("hvi_angle", "x.3a : non 45 degree angle hvi")
-hvntm.ongrid(0.005).output("hvntm_OFFGRID", "x.1b : OFFGRID vertex on hvntm")
-hvntm.with_angle(0 .. 45).output("hvntm_angle", "x.3a : non 45 degree angle hvntm")
-vhvi.ongrid(0.005).output("vhvi_OFFGRID", "x.1b : OFFGRID vertex on vhvi")
-vhvi.with_angle(0 .. 45).output("vhvi_angle", "x.3a : non 45 degree angle vhvi")
-uhvi.ongrid(0.005).output("uhvi_OFFGRID", "x.1b : OFFGRID vertex on uhvi")
-uhvi.with_angle(0 .. 45).output("uhvi_angle", "x.3a : non 45 degree angle uhvi")
-pwell_rs.ongrid(0.005).output("pwell_rs_OFFGRID", "x.1b : OFFGRID vertex on pwell_rs")
-pwell_rs.with_angle(0 .. 45).output("pwell_rs_angle", "x.3a : non 45 degree angle pwell_rs")
-areaid_re.ongrid(0.005).output("areaid_re_OFFGRID", "x.1b : OFFGRID vertex on areaid.re")
-
-end #OFFGRID
diff --git a/sky130/klayout/sky130A_non-mr.drc b/sky130/klayout/sky130A_non-mr.drc
deleted file mode 100644
index 0a0bc78..0000000
--- a/sky130/klayout/sky130A_non-mr.drc
+++ /dev/null
@@ -1,222 +0,0 @@
-#  DRC  for  SKY130 according to :
-#   https://skywater-pdk.readthedocs.io/en/latest/rules/periphery.html
-#   https://skywater-pdk.readthedocs.io/en/latest/rules/layers.html
-#
-#   Distributed under GNU GPLv3: https://www.gnu.org/licenses/
-#
-#  History :
-#   2022-01-13 : 2022.1.13_01.01 initial release
-#
-##########################################################################################
-
-# optionnal for a batch launch :   klayout -b -rd input=my_layout.gds -rd report=sky130_drc.txt -r drc_sky130.drc
-if $input
-  source($input, $top_cell)
-end
-
-if $report
-  report("SKY130 DRC runset", $report)
-else
-  report("SKY130 DRC runset", File.join(File.dirname(RBA::CellView::active.filename), "sky130_drc.txt"))
-end
-
-AL = true  # do not change
-CU = false  # do not change
-# choose betwen only one of  AL  or  CU  back-end flow here :
-backend_flow = AL
-
-# enable / disable rule groups
-if $feol  == "1"        || $feol == "true"
-  FEOL         = true # front-end-of-line checks
-else
-  FEOL         = false
-end
-
-if $beol == "1"         || $beol == "true"
-  BEOL         = true # back-end-of-line checks
-else
-  BEOL         = false
-end
-
-if $offgrid == "1"      || $offgrid == "true"
-  OFFGRID      = true # manufacturing grid/angle checks
-else
-  OFFGRID      = false
-end
-
-if $seal  == "1"        || $seal == "true"
-  SEAL         = true # SEAL RING checks
-else
-  SEAL         = false
-end
-
-if $floating_met == "1" || $floating_met == "true"
-  FLOATING_MET = true # back-end-of-line checks
-else
-  FLOATING_MET = false
-end
-
-# klayout setup
-########################
-# use a tile size of 1mm - not used in deep mode-
-# tiles(1000.um)
-# use a tile border of 10 micron:
-# tile_borders(1.um)
-#no_borders
-
-# hierachical
-deep
-
-if $thr
-    threads($thr)
-else
-    threads(4)
-end
-
-# if more inof is needed, set true
-# verbose(true)
-verbose(true)
-
-# layers definitions
-########################
-
-# all except purpose (datatype) 5 -- label and 44 -- via
-li_wildcard = "67/20"
-mcon_wildcard = "67/44"
-
-m1_wildcard = "68/20"
-via_wildcard = "68/44"
-
-m2_wildcard = "69/20"
-via2_wildcard = "69/44"
-
-m3_wildcard = "70/20"
-via3_wildcard = "70/44"
-
-m4_wildcard = "71/20"
-via4_wildcard = "71/44"
-
-m5_wildcard = "72/20"
-
-diff = input(65, 20)
-tap = polygons(65, 44)
-nwell = polygons(64, 20)
-dnwell = polygons(64, 18)
-pwbm = polygons(19, 44)
-pwde = polygons(124, 20)
-natfet = polygons(124, 21)
-hvtr = polygons(18, 20)
-hvtp = polygons(78, 44)
-ldntm = polygons(11, 44)
-hvi = polygons(75, 20)
-tunm = polygons(80, 20)
-lvtn = polygons(125, 44)
-poly = polygons(66, 20)
-hvntm = polygons(125, 20)
-nsdm = polygons(93, 44)
-psdm = polygons(94, 20)
-rpm = polygons(86, 20)
-urpm = polygons(79, 20)
-npc = polygons(95, 20)
-licon = polygons(66, 44)
-
-li = polygons(li_wildcard)
-mcon = polygons(mcon_wildcard)
-
-m1 = polygons(m1_wildcard)
-via = polygons(via_wildcard)
-
-m2 = polygons(m2_wildcard)
-via2 = polygons(via2_wildcard)
-
-m3 = polygons(m3_wildcard)
-via3 = polygons(via3_wildcard)
-
-m4 = polygons(m4_wildcard)
-via4 = polygons(via4_wildcard)
-
-m5 = polygons(m5_wildcard)
-
-pad = polygons(76, 20)
-nsm = polygons(61, 20)
-capm = polygons(89, 44)
-cap2m = polygons(97, 44)
-vhvi = polygons(74, 21)
-uhvi = polygons(74, 22)
-npn = polygons(82, 20)
-inductor = polygons(82, 24)
-vpp = polygons(82, 64)
-pnp = polygons(82, 44)
-lvs_prune = polygons(84, 44)
-ncm = polygons(92, 44)
-padcenter = polygons(81, 20)
-mf = polygons(76, 44)
-areaid_sl = polygons(81, 1)
-areaid_ce = polygons(81, 2)
-areaid_fe = polygons(81, 3)
-areaid_sc = polygons(81, 4)
-areaid_sf = polygons(81, 6)
-areaid_sw = polygons(81, 7)
-areaid_sr = polygons(81, 8)
-areaid_mt = polygons(81, 10)
-areaid_dt = polygons(81, 11)
-areaid_ft = polygons(81, 12)
-areaid_ww = polygons(81, 13)
-areaid_ld = polygons(81, 14)
-areaid_ns = polygons(81, 15)
-areaid_ij = polygons(81, 17)
-areaid_zr = polygons(81, 18)
-areaid_ed = polygons(81, 19)
-areaid_de = polygons(81, 23)
-areaid_rd = polygons(81, 24)
-areaid_dn = polygons(81, 50)
-areaid_cr = polygons(81, 51)
-areaid_cd = polygons(81, 52)
-areaid_st = polygons(81, 53)
-areaid_op = polygons(81, 54)
-areaid_en = polygons(81, 57)
-areaid_en20 = polygons(81, 58)
-areaid_le = polygons(81, 60)
-areaid_hl = polygons(81, 63)
-areaid_sd = polygons(81, 70)
-areaid_po = polygons(81, 81)
-areaid_it = polygons(81, 84)
-areaid_et = polygons(81, 101)
-areaid_lvt = polygons(81, 108)
-areaid_re = polygons(81, 125)
-areaid_ag = polygons(81, 79)
-poly_rs = polygons(66, 13)
-diff_rs = polygons(65, 13)
-pwell_rs = polygons(64, 13)
-li_rs = polygons(67, 13)
-cfom = polygons(22, 20)
-
-
-# Define a new custom function that selects polygons by their number of holes:
-# It will return a new layer containing those polygons with min to max holes.
-# max can be nil to omit the upper limit.
-class DRC::DRCLayer
-  def with_holes(min, max)
-    new_data = RBA::Region::new
-    self.data.each do |p|
-      if p.holes >= (min || 0) && (!max || p.holes <= max)
-        new_data.insert(p)
-      end
-    end
-    DRC::DRCLayer::new(@engine, new_data)
-  end
-end
-
-# DRC section
-########################
-log("DRC section")
-
-if FEOL
-log("FEOL section")
-
-#   nsdm
-log("START: 93/44 (nsdm)")
-nsdm.and(psdm).output("nsd.1", "nsd.1 : nsdm should not overlap psdm")
-log("END: 93/44 (nsdm)")
-
-end #FEOL
diff --git a/sky130/magic/sky130.tcl b/sky130/magic/sky130.tcl
index 89717de..5fb96a6 100644
--- a/sky130/magic/sky130.tcl
+++ b/sky130/magic/sky130.tcl
@@ -73,7 +73,7 @@
     dict set ruleset metal_spacing    0.23      ;# Local interconnect spacing rule
     dict set ruleset mmetal_spacing   0.14      ;# Metal spacing rule (above local interconnect)
     dict set ruleset res_to_cont      0.20      ;# resistor to contact center
-    dict set ruleset res_diff_space   0.20      ;# resistor to guard ring
+    dict set ruleset res_diff_spacing 0.20      ;# resistor to guard ring
 }
 
 #-----------------------------------------------------
@@ -2454,7 +2454,7 @@
 		compatible {sky130_fd_pr__res_high_po_0p35 \
 		sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \
 		sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \
-		full_metal 1 wmax 0.350 vias 1 n_guard 0 hv_guard 0 \
+		snake 0 full_metal 1 wmax 0.350 vias 1 n_guard 0 hv_guard 0 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_high_po_0p69_defaults {} {
@@ -2464,7 +2464,7 @@
 		compatible {sky130_fd_pr__res_high_po_0p35 \
 		sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \
 		sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \
-		full_metal 1 wmax 0.690 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 wmax 0.690 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_high_po_1p41_defaults {} {
@@ -2474,7 +2474,7 @@
 		compatible {sky130_fd_pr__res_high_po_0p35 \
 		sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \
 		sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \
-		full_metal 1 wmax 1.410 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 wmax 1.410 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_high_po_2p85_defaults {} {
@@ -2484,7 +2484,7 @@
 		compatible {sky130_fd_pr__res_high_po_0p35 \
 		sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \
 		sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \
-		full_metal 1 wmax 2.850 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 wmax 2.850 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_high_po_5p73_defaults {} {
@@ -2494,7 +2494,7 @@
 		compatible {sky130_fd_pr__res_high_po_0p35 \
 		sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \
 		sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \
-		full_metal 1 wmax 5.730 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 wmax 5.730 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 
@@ -2507,7 +2507,7 @@
 		compatible {sky130_fd_pr__res_xhigh_po_0p35 \
 		sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \
 		sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \
-		full_metal 1 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_xhigh_po_0p69_defaults {} {
@@ -2518,7 +2518,7 @@
 		compatible {sky130_fd_pr__res_xhigh_po_0p35 \
 		sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \
 		sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \
-		full_metal 1 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_xhigh_po_1p41_defaults {} {
@@ -2529,7 +2529,7 @@
 		compatible {sky130_fd_pr__res_xhigh_po_0p35 \
 		sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \
 		sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \
-		full_metal 1 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_xhigh_po_2p85_defaults {} {
@@ -2540,7 +2540,7 @@
 		compatible {sky130_fd_pr__res_xhigh_po_0p35 \
 		sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \
 		sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \
-		full_metal 1 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 proc sky130::sky130_fd_pr__res_xhigh_po_5p73_defaults {} {
@@ -2551,7 +2551,7 @@
 		compatible {sky130_fd_pr__res_xhigh_po_0p35 \
 		sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \
 		sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \
-		full_metal 1 n_guard 0 hv_guard 0 vias 1 \
+		snake 0 full_metal 1 n_guard 0 hv_guard 0 vias 1 \
 		viagb 0 viagt 0 viagl 0 viagr 0}
 }
 
@@ -3183,7 +3183,9 @@
     box grow w ${hw}um
     pushbox
     box grow s ${mask_clearance}um
-    paint ${res_type}
+    if {${mask_clearance} > 0} {
+        paint ${res_type}
+    }
     popbox
     box move s ${mask_clearance}um
     box grow s ${res_to_endcont}um
@@ -3211,7 +3213,15 @@
     set deltax [+ ${res_spacing} ${w}]
     set deltay [- ${l} ${w}]
     for {set i 0} {$i < [- $nf 1]} {incr i} {
-	paint ${res_type}
+	# Really should be drawing endcaps last instead of working around 1st one
+	if {($i == 0) && (${mask_clearance} < 0)} {
+	    pushbox
+	    box grow s ${mask_clearance}um
+	    paint ${res_type}
+	    popbox
+	} else {
+	    paint ${res_type}
+	}
  	pushbox
 	if {[% $i 2] == 0} {
 	    box move n ${deltay}um
@@ -3265,7 +3275,9 @@
     box grow w ${hw}um
     pushbox
     box grow $dir ${mask_clearance}um
-    paint ${res_type}
+    if {${mask_clearance} > 0} {
+        paint ${res_type}
+    }
     popbox
     box move $dir ${mask_clearance}um
     box grow $dir ${res_to_endcont}um
@@ -3463,7 +3475,8 @@
 	    end_surround	$poly_surround \
 	    end_spacing		0.48 \
 	    end_to_end_space	0.52 \
-	    res_to_endcont	$res_to_cont \
+	    res_to_cont		0.575 \
+	    res_to_endcont	1.985 \
 	    res_spacing		$poly_spacing \
 	    res_diff_spacing	0.48 \
 	    mask_clearance	0.52 \
@@ -3538,10 +3551,11 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
-	    mask_clearance	0.52 \
+	    mask_clearance	-2.16 \
 	    overlap_compress	0.36 \
     ]
     set drawdict [dict merge $sky130::ruleset $newdict $parameters]
@@ -3610,8 +3624,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -3682,8 +3697,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -3754,8 +3770,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -3826,8 +3843,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -3900,8 +3918,84 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
+	    res_diff_spacing	$gresdiff_spacing \
+	    mask_clearance	0.52 \
+	    overlap_compress	0.36 \
+    ]
+    set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+    return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::sky130_fd_pr__res_xhigh_po_0p35_draw {parameters} {
+
+    # Set a local variable for each rule in ruleset
+    foreach key [dict keys $sky130::ruleset] {
+        set $key [dict get $sky130::ruleset $key]
+    }
+
+    # Handle options related to guard ring type (high/low voltage, nwell/psub)
+    if {[dict exists $parameters hv_guard]} {
+	set use_hv_guard [dict get $parameters hv_guard]
+    } else {
+	set use_hv_guard 0
+    }
+    if {[dict exists $parameters n_guard]} {
+	set use_n_guard [dict get $parameters n_guard]
+    } else {
+	set use_n_guard 0
+    }
+
+    if {$use_hv_guard == 1} {
+	if {$use_n_guard == 1} {
+	    set gdifftype mvnsd
+	    set gdiffcont mvnsc
+	} else {
+	    set gdifftype mvpsd
+	    set gdiffcont mvpsc
+	}
+	set gsurround 0.33
+    } else {
+	if {$use_n_guard == 1} {
+	    set gdifftype nsd
+	    set gdiffcont nsc
+	} else {
+	    set gdifftype psd
+	    set gdiffcont psc
+	}
+	set gsurround $sub_surround
+    }
+    if {$use_n_guard == 1} {
+	set gsubtype nwell
+	set gresdiff_spacing 0.785
+	set gresdiff_end 0.525
+    } else {
+	set gsubtype psub
+	set gresdiff_spacing 0.48
+	set gresdiff_end 0.48
+    }
+
+    set newdict [dict create \
+	    res_type		xpres \
+	    res_idtype		res0p35 \
+	    end_type 		xpc \
+	    end_contact_type	xpc \
+	    end_contact_size	0 \
+	    plus_diff_type	$gdifftype \
+	    plus_contact_type	$gdiffcont \
+	    sub_type		$gsubtype \
+	    guard_sub_surround	$gsurround \
+	    end_surround	$poly_surround \
+	    end_spacing		$gresdiff_end \
+	    end_to_end_space	0.52 \
+	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
+	    res_to_endcont	1.985 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -3972,8 +4066,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -4044,8 +4139,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -4116,8 +4212,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
@@ -4188,8 +4285,9 @@
 	    end_spacing		$gresdiff_end \
 	    end_to_end_space	0.52 \
 	    end_contact_size	0.19 \
+	    res_to_cont		0.575 \
 	    res_to_endcont	1.985 \
-	    res_spacing		1.24 \
+	    res_spacing		0.48 \
 	    res_diff_spacing	$gresdiff_spacing \
 	    mask_clearance	0.52 \
 	    overlap_compress	0.36 \
diff --git a/sky130/magic/sky130.tech b/sky130/magic/sky130.tech
index 46bceb1..76e92f8 100644
--- a/sky130/magic/sky130.tech
+++ b/sky130/magic/sky130.tech
@@ -23,7 +23,7 @@
 version
  version REVISION
  description "SkyWater SKY130: Open Source rules and DRC"
- requires magic-8.3.277
+ requires magic-8.3.306
 end
 
 #------------------------------------------------------------------------
@@ -34,6 +34,7 @@
 # Status 1/3/21: Taking out of beta and declaring an official release.
 # Status 3/17/22: Added fringeshieldhalo to improve parasitic capacitance
 #		  calculations.
+# Status 5/21/22: Added sidewall edge (spacing) enlargement.
 #------------------------------------------------------------------------
 
 #------------------------------------------------------------------------
@@ -4371,11 +4372,12 @@
  overhang *poly allfetsstd,allfetsspecial 130 "poly overhang of transistor < %d (poly.8)"
  overhang *poly allfetscore 110 "poly overhang of SRAM core transistor < %d (poly.8)"
  rect_only allfets "No bends in transistors (poly.11)"
- rect_only xhrpoly,uhrpoly "No bends in poly resistors (poly.11)"
  extend  xpc/a xhrpoly,uhrpoly 2160 \
  	"poly contact extends poly resistor by < %d (licon.1c + li.5)"
- spacing xhrpoly,uhrpoly,xpc xhrpoly,uhrpoly,xpc 1240 touching_illegal \
-	"Distance between precision resistors < %d (rpm.2 + 2 * rpm.3)"
+ spacing xhrpoly,uhrpoly,xpc xhrpoly,uhrpoly,xpc 210 touching_illegal \
+	"Distance between precision resistors < %d (poly.2)"
+ spacing xhrpoly,uhrpoly,xpc *poly,allfets,mrp1,rmp 400 touching_illegal \
+	"Distance from precision resistor to poly < %d (rpm.7 + rpm.3)"
 
  variants (fast)
 
@@ -5431,13 +5433,13 @@
 # defaultareacap     allpolynonfet active  106
 # defaultperimeter   allpolynonfet active   57
 
- defaultsidewall    *poly active 17.0
+ defaultsidewall    *poly active 16.0
  defaultareacap     *poly active 106.13
  defaultperimeter   *poly active 55.27
  defaultsideoverlap *poly active nwell,pwell well  55.27
 
 #locali
- defaultsidewall    allli locali 28.0
+ defaultsidewall    allli locali 25.5	0.14
  defaultareacap     allli locali 36.99
  defaultperimeter   allli locali 40.70
  defaultoverlap     allli locali nwell,pwell well  36.99
@@ -5453,7 +5455,7 @@
  defaultsideoverlap *poly active allli locali 25.14
 
 #metal1
- defaultsidewall    allm1 metal1 36.9
+ defaultsidewall    allm1 metal1 44	0.25
  defaultareacap     allm1 metal1 25.78
  defaultperimeter   allm1 metal1 40.57
  defaultoverlap     allm1 metal1 nwell,pwell well  25.78
@@ -5474,7 +5476,7 @@
  defaultsideoverlap allli locali allm1 metal1 34.70
 
 #metal2
- defaultsidewall    allm2 metal2      39.1
+ defaultsidewall    allm2 metal2      50	0.3
 
 #ifdef RERAM
 # For ReRAM, all parasitics account for the additional 0.295um between
@@ -5505,9 +5507,9 @@
  defaultsideoverlap allm1 metal1 allm2 metal2 23.03
 
 #else (!RERAM)
- defaultareacap     allm2 metal2 16.9
+ defaultareacap     allm2 metal2 17.5
  defaultperimeter   allm2 metal2 37.76
- defaultoverlap     allm2 metal2 nwell,pwell well 16.9
+ defaultoverlap     allm2 metal2 nwell,pwell well 17.5
  defaultsideoverlap allm2 metal2 nwell,pwell well 37.76
 
 #metal2->diff
@@ -5532,7 +5534,7 @@
 #endif (!RERAM)
 
 #metal3
- defaultsidewall    allm3 metal3     56.2
+ defaultsidewall    allm3 metal3     74.0	0.40
 
 #ifdef RERAM
  defaultareacap     allm3 metal3 11.19
@@ -5593,7 +5595,7 @@
 
 #ifdef METAL5
 #metal4
- defaultsidewall    allm4 metal4 59.0
+ defaultsidewall    allm4 metal4 94.0	0.57
 #ifdef RERAM
  defaultareacap     allm4 metal4 7.84
  defaultperimeter   allm4 metal4 34.17
@@ -5657,7 +5659,7 @@
  defaultsideoverlap allm3 metal3 allm4 metal4 42.64
 
 #metal5
- defaultsidewall    allm5 metal5 95.1
+ defaultsidewall    allm5 metal5 155	0.5
 #ifdef RERAM
  defaultareacap     allm5 metal5 5.99
  defaultperimeter   allm5 metal5 36.83
@@ -5761,13 +5763,13 @@
 # defaultareacap     allpolynonfet active  80.4
 # defaultperimeter   allpolynonfet active   57
 
- defaultsidewall    *poly active 17.0
+ defaultsidewall    *poly active 17.0	0.21
  defaultareacap     *poly active 80.4
  defaultperimeter   *poly active 48.83
  defaultsideoverlap *poly active nwell,pwell well  48.83
 
 #locali
- defaultsidewall    allli locali 26.7
+ defaultsidewall    allli locali 26.7	0.17
  defaultareacap     allli locali 29.3
  defaultperimeter   allli locali 35.69
  defaultoverlap     allli locali nwell,pwell well  29.3
@@ -5783,7 +5785,7 @@
  defaultsideoverlap *poly active allli locali 21.21
 
 #metal1
- defaultsidewall    allm1 metal1 35.6
+ defaultsidewall    allm1 metal1 35.6	0.14
  defaultareacap     allm1 metal1 20.2
  defaultperimeter   allm1 metal1 34.41
  defaultoverlap     allm1 metal1 nwell,pwell well  20.2
@@ -5804,7 +5806,7 @@
  defaultsideoverlap allli locali allm1 metal1 28.84
 
 #metal2
- defaultsidewall    allm2 metal2      37.6
+ defaultsidewall    allm2 metal2      37.61	0.14
 
 #ifdef RERAM
 # For ReRAM, all parasitics account for the additional 0.295um between
@@ -5862,7 +5864,7 @@
 #endif (!RERAM)
 
 #metal3
- defaultsidewall    allm3 metal3     52.0
+ defaultsidewall    allm3 metal3     52.0	0.30
 
 #ifdef RERAM
  defaultareacap     allm3 metal3 9.84
@@ -5923,7 +5925,7 @@
 
 #ifdef METAL5
 #metal4
- defaultsidewall    allm4 metal4 53.7
+ defaultsidewall    allm4 metal4 53.7	0.30
 #ifdef RERAM
  defaultareacap     allm4 metal4 6.92
  defaultperimeter   allm4 metal4 30.18
@@ -5987,7 +5989,7 @@
  defaultsideoverlap allm3 metal3 allm4 metal4 35.93
 
 #metal5
- defaultsidewall    allm5 metal5 81.5
+ defaultsidewall    allm5 metal5 81.5	1.6
 #ifdef RERAM
  defaultareacap     allm5 metal5 5.34
  defaultperimeter   allm5 metal5 32.85
@@ -6091,13 +6093,13 @@
 # defaultareacap     allpolynonfet active  106
 # defaultperimeter   allpolynonfet active   57
 
- defaultsidewall    *poly active 17.0
+ defaultsidewall    *poly active 17.0	0.21
  defaultareacap     *poly active 155.0
  defaultperimeter   *poly active 64.82
  defaultsideoverlap *poly active nwell,pwell well  64.82
 
 #locali
- defaultsidewall    allli locali 28.7
+ defaultsidewall    allli locali 28.7	0.17
  defaultareacap     allli locali 49.7
  defaultperimeter   allli locali 47.02
  defaultoverlap     allli locali nwell,pwell well  49.7
@@ -6113,7 +6115,7 @@
  defaultsideoverlap *poly active allli locali 32.29
 
 #metal1
- defaultsidewall    allm1 metal1 37.6
+ defaultsidewall    allm1 metal1 37.6	0.14
  defaultareacap     allm1 metal1 35.7
  defaultperimeter   allm1 metal1 49.59
  defaultoverlap     allm1 metal1 nwell,pwell well  35.7
@@ -6134,7 +6136,7 @@
  defaultsideoverlap allli locali allm1 metal1 46.73
 
 #metal2
- defaultsidewall    allm2 metal2      40.2
+ defaultsidewall    allm2 metal2      40.2	0.14
 
 #ifdef RERAM
 # For ReRAM, all parasitics account for the additional 0.295um between
@@ -6192,7 +6194,7 @@
 #endif (!RERAM)
 
 #metal3
- defaultsidewall    allm3 metal3     60.6
+ defaultsidewall    allm3 metal3     60.6	0.30
 
 #ifdef RERAM
  defaultareacap     allm3 metal3 14.74
@@ -6253,7 +6255,7 @@
 
 #ifdef METAL5
 #metal4
- defaultsidewall    allm4 metal4 65.4
+ defaultsidewall    allm4 metal4 65.4	0.30
 #ifdef RERAM
  defaultareacap     allm4 metal4 9.97
  defaultperimeter   allm4 metal4 39.49
@@ -6317,7 +6319,7 @@
  defaultsideoverlap allm3 metal3 allm4 metal4 55.70
 
 #metal5
- defaultsidewall    allm5 metal5 118.7
+ defaultsidewall    allm5 metal5 118.7	1.6
 #ifdef RERAM
  defaultareacap     allm5 metal5 7.36
  defaultperimeter   allm5 metal5 42.58
diff --git a/sky130/netgen/sky130_setup.tcl b/sky130/netgen/sky130_setup.tcl
index 6f217f6..23567d8 100644
--- a/sky130/netgen/sky130_setup.tcl
+++ b/sky130/netgen/sky130_setup.tcl
@@ -131,6 +131,7 @@
 lappend devices sky130_fd_pr__nfet_01v8_lvt
 lappend devices sky130_fd_bs_flash__special_sonosfet_star
 lappend devices sky130_fd_pr__nfet_g5v0d10v5
+lappend devices sky130_fd_pr__nfet_03v3_nvt
 lappend devices sky130_fd_pr__nfet_05v0_nvt
 lappend devices sky130_fd_pr__pfet_01v8
 lappend devices sky130_fd_pr__pfet_01v8_lvt
diff --git a/sky130/sky130.json b/sky130/sky130.json
index 97a74ad..56ba0b8 100644
--- a/sky130/sky130.json
+++ b/sky130/sky130.json
@@ -88,4 +88,15 @@
 	"open_pdks": "OPEN_PDKS_COMMIT",
 	"magic": "MAGIC_COMMIT"
     }
+    "reference": {
+	"open_pdks": "d7faec2b6f384254449e0172c4f26083f77d3ff5",
+	"magic": "fe2eb6d3906ed15ade0e7a51daea80dd4e3846e2",
+	"skywater_pdk": "f70d8ca46961ff92719d8870a18a076370b85f6c",
+	"sky130_osu_sc_t12": "6af093f919721daec4bb256c4c40aaa8bc84f4bd",
+	"sky130_osu_sc_t15": "f1eef844734f73d3c79d83b82352118263eb7686",
+	"sky130_osu_sc_t18": "3128b623aaea315248d39173e09b49a3dc82aa40",
+	"sky130_sram_macros": "c2333394e0b0b9d9d71185678a8d8087715d5e3b",
+	"sky130_ml_xx_hd": "6eb3b0718552b034f1bf1870285ff135e3fb2dcb"
+	"xschem_sky130": "16efae642739ba5c50aa2a40e403b036a5e31a6c"
+    }
 }