Completed the first full implementation of the LDNMOS and LDPMOS
(16V) devices in the magic tech file and device generator for
sky130.  This includes validating the device generator for
fingered devices and multiple devices, with and without
overlapping diffusions, and cifinput rules for reading the
devices from GDS and producing a valid database in magic from
which the device can again be extracted.
diff --git a/sky130/magic/sky130.tcl b/sky130/magic/sky130.tcl
index 69465d4..c4d8517 100644
--- a/sky130/magic/sky130.tcl
+++ b/sky130/magic/sky130.tcl
@@ -5786,19 +5786,21 @@
     box grow n ${hw}um
     box grow s ${hw}um
     box move $dside ${hl}um
+    pushbox
     box grow $dside ${extension}um
     paint ed
     set cext [sky130::getbox]
-    box width 0.26um
-    box move $dside 0.26um
+    popbox
+    box move $dside ${extension}um
+    box move $dside 0.13um
+    box grow e 0.13um
+    box grow w 0.13um
     paint mvnsd
+    # Test:  Do not include nwell in bounding box
+    set cext [sky130::unionbox $cext [sky130::getbox]]
     pushbox
     box grow c 0.66um
     paint nwell
-    # Force box 0.48um larger so that spacing to guard ring
-    # comes out to 0.86um.
-    box grow c 0.48um
-    set cext [sky130::unionbox $cext [sky130::getbox]]
     popbox
     popbox
     # Back to the center point
@@ -5836,7 +5838,7 @@
     if {$cdw > $cdwfull} [set cdw $cdwfull]
 
     set cext [sky130::unionbox $cext [sky130::draw_contact 0 ${cdw} \
-		0.045 ${metal_surround} ${contact_size}\
+		${drain_diff_surround} ${metal_surround} ${contact_size}\
 		mvnsd mvnsc li vert]]
     popbox
     return $cext
@@ -5866,36 +5868,24 @@
     # Starts with point at the device center
     box grow n ${hw}um
     box grow s ${hw}um
-    # Draw nwell under the entire device
-    pushbox
-    box grow e ${hl}um
-    box grow w ${hl}um
-    # Well should cover device and meet well width on
-    # top and bottom of the area cut out for the drain
-    box grow c 0.84um
-    box grow c 0.86um
-    box grow $dside ${extension}um
-    box grow $dside 0.26um
-    box grow $sside -1.08um
-    paint nwell
-    popbox
     box move $dside ${hl}um
+    pushbox
     box grow $dside ${extension}um
     paint ed
     set cext [sky130::getbox]
-    box width 0.26um
-    box move $dside 0.26um
+    popbox
+    box move $dside ${extension}um
+    box move $dside 0.13um
+    box grow e 0.13um
+    box grow w 0.13um
     paint mvpsd
     pushbox
     box grow c 0.86um
-    # Shorter on gate side to avoid DRC error;  actual
+    # Shorter than necessary to avoid DRC error;  actual
     # nwell is erased under gate by GDS generation rules.
-    box grow $sside -0.13um
+    box grow e -0.15um
+    box grow w -0.15um
     paint pwell
-    # Force box 0.28um larger so that spacing to guard ring
-    # comes out to 0.66um.
-    box grow c 0.28um
-    set cext [sky130::unionbox $cext [sky130::getbox]]
     popbox
     popbox
     # Back to the center point
@@ -5933,7 +5923,7 @@
     if {$cdw > $cdwfull} [set cdw $cdwfull]
 
     set cext [sky130::unionbox $cext [sky130::draw_contact 0 ${cdw} \
-		0.045 ${metal_surround} ${contact_size}\
+		${drain_diff_surround} ${metal_surround} ${contact_size}\
 		mvpsd mvpsc li vert]]
     popbox
     return $cext
@@ -6313,11 +6303,19 @@
     set id2_type ""	;# additional type covering everything
     set id2_surround 0	;# amount of surround on above type
 
+    set set_x_to_guard ""	;# override x distance to guard ring
+    set set_y_to_guard ""	;# override y distance to guard ring
+
     # Set a local variable for each parameter (e.g., $l, $w, etc.)
     foreach key [dict keys $parameters] {
         set $key [dict get $parameters $key]
     }
 
+    # Diff surround on drain is by default the same as diff surround
+    if {![dict exist $parameters drain_diff_surround]} {
+	set drain_diff_surround $diff_surround
+    }
+
     # Diff-to-tap spacing is by default the same as diff spacing
     if {![dict exist $parameters diff_tap_space]} {
 	set diff_tap_space $diff_spacing
@@ -6361,7 +6359,7 @@
 
     tech lock *
     set bbox [sky130::mos_device $parameters]
-    # puts stdout "Diagnostic: Device bounding box e $bbox (um)"
+    puts stdout "Diagnostic: Device bounding box e $bbox (um)"
     tech unlock *
 
     set fw [- [lindex $bbox 2] [lindex $bbox 0]]
@@ -6369,6 +6367,10 @@
     set lw [+ [lindex $bbox 2] [lindex $bbox 0]]
     set lh [+ [lindex $bbox 3] [lindex $bbox 1]]
 
+    # If the bounding box is not symmetric about x=0, then find the
+    # offset.  Assumed to be needed only for X (asymmetric drain)
+    set xoffset [+ [lindex $bbox 0] [lindex $bbox 2]]
+    
     # If dev_sub_dist > 0 then each device must be in its own substrate
     # (well) area, and overlaps are disallowed.  dev_sub_space determines
     # the distance between individual devices in an array.
@@ -6399,7 +6401,7 @@
 	    set dx [+ $fw $diff_spacing]
 	} else {
 	    # overlap diffusions
-	    set dx [- $fw [+ $diff_surround $diff_surround $contact_size]]
+	    set dx [- $fw [+ $drain_diff_surround $drain_diff_surround $contact_size]]
 	}
     }
 
@@ -6450,6 +6452,14 @@
 		set corelly [- $corelly [/ $sdiff 2.0]]
 	    }
 	}
+
+	# set_x|y_to_guard overrides the above calculations if present.
+	if {$set_x_to_guard != ""} {
+	    set gx [+ $corex [* 2.0 $set_x_to_guard]]
+	}
+	if {$set_y_to_guard != ""} {
+	    set gy [+ $corey [* 2.0 $set_y_to_guard]]
+	}
     }
     if {$guard != 0} {
 	# Draw the guard ring first, as MOS well may interact with guard ring substrate
@@ -6488,7 +6498,9 @@
 	    set saveeo $evenodd
 	}
         for {set yp 0} {$yp < $m} {incr yp} {
+            if {$evens != 0} {box move e ${xoffset}um}
             sky130::mos_device $parameters
+            if {$evens != 0} {box move w ${xoffset}um}
             box move n ${dy}um
 	    if {$intc == 1} {
 		set evenodd [- 1 $evenodd]
@@ -6808,6 +6820,9 @@
 	    diff_spacing	0.31 \
 	    diff_tap_space	0.38 \
 	    diff_gate_space	0.38 \
+	    drain_diff_surround 0.045 \
+	    set_x_to_guard	1.665 \
+	    set_y_to_guard	1.225 \
 	    drain_proc		sky130::draw_ldnmos_drain \
     ]
     set drawdict [dict merge $sky130::ruleset $newdict $parameters]
@@ -6824,11 +6839,16 @@
 	    poly_type		poly \
 	    poly_contact_type	pc \
 	    sub_type		nwell \
+	    id_type		nwell \
+	    id_surround		0.035 \
 	    guard_sub_surround	0.33 \
 	    gate_to_polycont	0.32 \
 	    diff_spacing	0.31 \
 	    diff_tap_space	0.38 \
 	    diff_gate_space	0.38 \
+	    drain_diff_surround 0.045 \
+	    set_x_to_guard	1.665 \
+	    set_y_to_guard	1.180 \
 	    drain_proc		sky130::draw_ldpmos_drain \
     ]
     set drawdict [dict merge $sky130::ruleset $newdict $parameters]
diff --git a/sky130/magic/sky130.tech b/sky130/magic/sky130.tech
index 41e13ff..df1f908 100644
--- a/sky130/magic/sky130.tech
+++ b/sky130/magic/sky130.tech
@@ -1136,7 +1136,9 @@
 # HVI (includes rules NWELL 8-11 and DIFFTAP 14-26)
 #----------------------------------------------------------------
 
- templayer thkox_area 	alldiffmv,mvvar
+ templayer thkox_area ed
+	grow 475
+	or alldiffmv,mvvar
 	grow	185
 	bloat-all alldiffmv nwell
 	grow 345
@@ -2987,6 +2989,47 @@
  labels TAP
  labels TAPTXT text
 
+ # Fill in FET under extended drain
+ layer mvpfet EDID
+ and POLY
+ and-not DIFF
+ and-not TAP
+ and-not NWELL
+
+ layer mvnfet EDID
+ and POLY
+ and-not DIFF
+ and-not TAP
+ and NWELL
+
+ # Restrict where nwell is merged
+ templayer ldmos_nwell EDID
+ grow 1200
+ and NWELL
+
+ # Remove or add well under extended FET gate
+ layer nwell EDID
+ and POLY
+ and DIFF
+ and PSDM
+ grow 685
+ or ldmos_nwell
+ grow 420
+ shrink 420
+
+ layer pwell EDID
+ and POLY
+ and DIFF
+ and NSDM
+ grow 660
+ grow 420
+ shrink 420
+
+ layer ed EDID
+ and-not POLY
+ and-not DIFF
+ and-not TAP
+
  templayer mvnsdexpand mvnsdarea
  grow 500
 
@@ -3977,6 +4020,8 @@
  calma PSDM 94 20
  # HVI (THKOX)
  calma HVI 75 20
+ # EDID
+ calma EDID 81 57
  # NPC
  calma NPC 95 20
  # P+ POLY MASK