Added an option to all MOSFETs to connect gates together
(horizontally).  In the case where the gate is so short that
adjacent gates have DRC spacing errors on poly, the poly is merged,
and the script does not attempt to alternate top and bottom poly
contacts to avoid a DRC error.  In all cases, the local interconnect
and (optionally) metal are connected across the top and/or bottom
gate contacts.  This option has been lightly tested but appears to
work cleanly with all geometries.
diff --git a/sky130/magic/sky130.tcl b/sky130/magic/sky130.tcl
index 30aa321..45b64e1 100644
--- a/sky130/magic/sky130.tcl
+++ b/sky130/magic/sky130.tcl
@@ -5426,7 +5426,7 @@
 		topc 1 botc 1 poverlap 0 doverlap 1 lmin 0.15 wmin 0.42 \
 		class mosfet compatible {sky130_fd_pr__pfet_01v8 \
 		sky130_fd_pr__pfet_01v8_lvt sky130_fd_pr__pfet_01v8_hvt \
-		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 \
+		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 conn_gates 0 \
 		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
@@ -5437,7 +5437,7 @@
 		topc 1 botc 1 poverlap 0 doverlap 1 lmin 0.35 wmin 0.42 \
 		class mosfet compatible {sky130_fd_pr__pfet_01v8 \
 		sky130_fd_pr__pfet_01v8_lvt sky130_fd_pr__pfet_01v8_hvt \
-		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 \
+		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 conn_gates 0 \
 		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
@@ -5448,7 +5448,7 @@
 		topc 1 botc 1 poverlap 0 doverlap 1 lmin 0.15 wmin 0.42 \
 		class mosfet compatible {sky130_fd_pr__pfet_01v8 \
 		sky130_fd_pr__pfet_01v8_lvt sky130_fd_pr__pfet_01v8_hvt \
-		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 \
+		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 conn_gates 0 \
 		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
@@ -5459,7 +5459,7 @@
 		topc 1 botc 1 poverlap 0 doverlap 1 lmin 0.50 wmin 0.42 \
 		class mosfet compatible {sky130_fd_pr__pfet_01v8 \
 		sky130_fd_pr__pfet_01v8_lvt sky130_fd_pr__pfet_01v8_hvt \
-		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 \
+		sky130_fd_pr__pfet_g5v0d10v5} full_metal 1 conn_gates 0 \
 		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
@@ -5468,7 +5468,8 @@
     return {w 5.00 l 1.050 m 1 nf 1 diffcov 100 polycov 100 \
 		guard 1 glc 1 grc 1 gtc 1 gbc 1 tbcov 100 rlcov 100 \
 		topc 1 botc 1 poverlap 0 doverlap 0 lmin 1.050 wmin 5.00 \
-		class mosfet full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		class mosfet full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5485,8 +5486,8 @@
 		compatible {sky130_fd_pr__nfet_01v8 sky130_fd_pr__nfet_01v8_lvt \
 		sky130_fd_bs_flash__special_sonosfet_star \
 		sky130_fd_pr__nfet_g5v0d10v5 sky130_fd_pr__nfet_05v0_nvt \
-		sky130_fd_pr__nfet_03v3_nvt} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		sky130_fd_pr__nfet_03v3_nvt} full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5498,8 +5499,8 @@
 		compatible {sky130_fd_pr__nfet_01v8 sky130_fd_pr__nfet_01v8_lvt \
 		sky130_fd_bs_flash__special_sonosfet_star \
 		sky130_fd_pr__nfet_g5v0d10v5 sky130_fd_pr__nfet_05v0_nvt \
-		sky130_fd_pr__nfet_03v3_nvt} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		sky130_fd_pr__nfet_03v3_nvt} full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5511,8 +5512,8 @@
 		compatible {sky130_fd_pr__nfet_01v8 sky130_fd_pr__nfet_01v8_lvt \
 		sky130_fd_bs_flash__special_sonosfet_star \
 		sky130_fd_pr__nfet_g5v0d10v5 sky130_fd_pr__nfet_05v0_nvt \
-		sky130_fd_pr__nfet_03v3_nvt} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		sky130_fd_pr__nfet_03v3_nvt} full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5524,8 +5525,8 @@
 		compatible {sky130_fd_pr__nfet_01v8 sky130_fd_pr__nfet_01v8_lvt \
 		sky130_fd_bs_flash__special_sonosfet_star \
 		sky130_fd_pr__nfet_g5v0d10v5 sky130_fd_pr__nfet_05v0_nvt \
-		sky130_fd_pr__nfet_03v3_nvt} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		sky130_fd_pr__nfet_03v3_nvt} full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5537,8 +5538,8 @@
 		compatible {sky130_fd_pr__nfet_01v8 sky130_fd_pr__nfet_01v8_lvt \
 		sky130_fd_bs_flash__special_sonosfet_star \
 		sky130_fd_pr__nfet_g5v0d10v5 sky130_fd_pr__nfet_05v0_nvt \
-		sky130_fd_pr__nfet_03v3_nvt} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		sky130_fd_pr__nfet_03v3_nvt} full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5550,8 +5551,8 @@
 		compatible {sky130_fd_pr__nfet_01v8 sky130_fd_pr__nfet_01v8_lvt \
 		sky130_fd_bs_flash__special_sonosfet_star \
 		sky130_fd_pr__nfet_g5v0d10v5 sky130_fd_pr__nfet_05v0_nvt \
-		sky130_fd_pr__nfet_03v3_nvt} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		sky130_fd_pr__nfet_03v3_nvt} full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5559,7 +5560,8 @@
     return {w 5.00 l 1.055 m 1 nf 1 diffcov 100 polycov 100 \
 		guard 1 glc 1 grc 1 gtc 1 gbc 1 tbcov 100 rlcov 100 \
 		topc 1 botc 1 poverlap 0 doverlap 0 lmin 1.050 wmin 5.00 \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 doports 1}
 }
 
@@ -5584,7 +5586,8 @@
 		topc 1 botc 1 poverlap 0 doverlap 1 lmin 0.18 wmin 1.0 \
 		class mosfet compatible {sky130_fd_pr__cap_var_lvt \
 		sky130_fd_pr__cap_var_hvt sky130_fd_pr__cap_var} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 gshield 1}
 }
 
@@ -5594,7 +5597,8 @@
 		topc 1 botc 1 poverlap 0 doverlap 1 lmin 0.50 wmin 1.0 \
 		class mosfet compatible {sky130_fd_pr__cap_var_lvt \
 		sky130_fd_pr__cap_var_hvt sky130_fd_pr__cap_var} \
-		full_metal 1 viasrc 100 viadrn 100 viagate 100 \
+		full_metal 1 conn_gates 0 \
+		viasrc 100 viadrn 100 viagate 100 \
 		viagb 0 viagr 0 viagl 0 viagt 0 gshield 1}
 }
 
@@ -5739,6 +5743,9 @@
     magic::add_checkbox doverlap "Overlap at diffusion contact" $parameters
     magic::add_checkbox topc "Add top gate contact" $parameters
     magic::add_checkbox botc "Add bottom gate contact" $parameters
+    if {[dict exists $parameters conn_gates]} {
+	magic::add_checkbox conn_gates "Connect gates together" $parameters
+    }
 
     magic::add_checkbox guard "Add guard ring" $parameters
     magic::add_checkbox full_metal "Full metal guard ring" $parameters
@@ -6435,6 +6442,8 @@
     set min_effl 0	;# gate length below which finger pitch must be stretched
     set diff_overlap_cont 0	;# extra overlap of end contact by diffusion
     set gshield 0	;# no metal shield over gate (used for varactors)
+    set conn_gates 0	;# do not extend metal to connect to neighboring gates
+    set poly_merge 0	;# do not extend poly to connect to neighboring gates
     set drain_proc {}	;# no special procedure to draw the drain
 
     set drain ""
@@ -6710,11 +6719,35 @@
 	       box grow w [/ $cw 2]um
 	   }
            sky130::mcon_draw horz
-           popbox
+	   popbox
+	   pushbox
+
+	   if {$conn_gates == 1} {
+	       box grow n [/ $ch 2]um
+	       box grow s [/ $ch 2]um
+	       box grow w [+ ${he} ${gate_to_diffcont}]um
+	       box grow e [+ ${he} ${gate_to_diffcont}]um
+	       paint m1
+	   }
+	   popbox
        }
        set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
 		${poly_surround} ${metal_surround} ${contact_size} \
 		${poly_type} ${poly_contact_type} li horz]]
+       if {$conn_gates == 1} {
+	   pushbox
+	   box grow n [/ $contact_size 2]um
+	   box grow s [/ $contact_size 2]um
+	   box grow w [+ ${he} ${gate_to_diffcont}]um
+	   box grow e [+ ${he} ${gate_to_diffcont}]um
+	   paint li 
+	   if {$poly_merge == 1} {
+	      box grow n ${poly_surround}um
+	      box grow s ${poly_surround}um
+	      paint poly 
+	   }
+	   popbox
+       }
        if {$gate != ""} {
 	   label $gate c $poly_contact_type
 	   select area label
@@ -6749,10 +6782,34 @@
 	   }
            sky130::mcon_draw horz
            popbox
+	   pushbox
+
+	   if {$conn_gates == 1} {
+	       box grow n [/ $ch 2]um
+	       box grow s [/ $ch 2]um
+	       box grow w [+ ${he} ${gate_to_diffcont}]um
+	       box grow e [+ ${he} ${gate_to_diffcont}]um
+	       paint m1
+	   }
+	   popbox
        }
        set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
 		${poly_surround} ${metal_surround} ${contact_size} \
 		${poly_type} ${poly_contact_type} li horz]]
+       if {$conn_gates == 1} {
+	   pushbox
+	   box grow n [/ $contact_size 2]um
+	   box grow s [/ $contact_size 2]um
+	   box grow w [+ ${he} ${gate_to_diffcont}]um
+	   box grow e [+ ${he} ${gate_to_diffcont}]um
+	   paint li 
+	   if {$poly_merge == 1} {
+	      box grow n ${poly_surround}um
+	      box grow s ${poly_surround}um
+	      paint poly 
+	   }
+	   popbox
+       }
        if {($gate != "") && ($topc == 0)} {
 	   label $gate c $poly_contact_type
 	   select area label
@@ -6815,6 +6872,8 @@
     set id_surround 0	;# amount of surround on above type
     set id2_type ""	;# additional type covering everything
     set id2_surround 0	;# amount of surround on above type
+    set conn_gates 0	;# do not connect multiple gates together
+    set poly_merge 0	;# do not merge poly between gates
     set doports 0	;# no port labels unless requested
 
     set set_x_to_guard ""	;# override x distance to guard ring
@@ -6851,22 +6910,33 @@
     box values 0 0 0 0
 
     # If dx < (poly contact space + poly contact width), then there is not
-    # enough room for a row of contacts, so force alternating contacts
+    # enough room for a row of contacts, so force alternating contacts;
+    # However, if conn_gates is set, just set a flag noting that poly
+    # needs to be drawn over all of the gates, connecting them together,
+    # but the positions will not alternate.
 
     set evens 1
     if {$nf > 1 && $l < $min_allc} {
-	set intc 1
-	set evenodd 1
-	dict set parameters oldtopc $topc
-	dict set parameters oldbotc $botc
-	set topc 1
-	set botc 1
-	dict set parameters topc 1
-	dict set parameters botc 1
-	set poverlap 0
+	if {$conn_gates == 1 } {
+	    set intc 0
+	    set poly_merge 1
+	} else {
+	    set intc 1
+	    set evenodd 1
+	    dict set parameters oldtopc $topc
+	    dict set parameters oldbotc $botc
+	    set topc 1
+	    set botc 1
+	    dict set parameters topc 1
+	    dict set parameters botc 1
+	    set poverlap 0
+	    set poly_merge 0
+	}
     } else {
 	set intc 0
+	set poly_merge 0
     }
+    dict set parameters poly_merge $poly_merge
 
     # Determine the base device dimensions by drawing one device
     # while all layers are locked (nothing drawn).  This allows the
@@ -6999,6 +7069,7 @@
     pushbox
     box move w ${corellx}um
     box move s ${corelly}um
+
     for {set xp 0} {$xp < $nf} {incr xp} {
 	dict set parameters evens $evens
 	set evens [- 1 $evens]
@@ -7032,7 +7103,11 @@
 		    } else {
 			dict set parameters source S$xp
 		    }
-		    dict set parameters gate G$xp
+		    if {$conn_gates == 0} {
+			dict set parameters gate G$xp
+		    } else {
+			dict set parameters gate G
+		    }
 		}
 	    } elseif {$doports} {		
 		if {$nf == 1} {
@@ -7057,11 +7132,19 @@
 			dict set parameters source S${xp}_$yp
 		    }
 		    if {$poverlap && ($yp == 0)} {
-			dict set parameters gate G$xp
+			if {$conn_gates == 0} {
+			    dict set parameters gate G$xp
+			} else {
+			    dict set parameters gate G
+			}
 		    } elseif {$poverlap} {
 			dict set parameters gate ""
 		    } else {
-			dict set parameters gate G${xp}_$yp
+			if {$conn_gates == 0} {
+			    dict set parameters gate G${xp}_$yp
+			} else {
+			    dict set parameters gate G$yp
+			}
 		    }
 		}
 	    }