Initial commit of public repository open_pdks.
diff --git a/sky130/sky130.tcl b/sky130/sky130.tcl
new file mode 100644
index 0000000..684aee5
--- /dev/null
+++ b/sky130/sky130.tcl
@@ -0,0 +1,5596 @@
+###
+### Source file sky130.tcl
+### Process this file with the preprocessor script
+###
+#-----------------------------------------------------
+# Magic/TCL design kit for SKYWATER TECHNAME
+#-----------------------------------------------------
+# Tim Edwards
+# Revision 0 PRE-ALPHA 3/21/2019
+#-----------------------------------------------------
+
+set TECHPATH STAGING_PATH
+if [catch {set PDKPATH}] {set PDKPATH ${TECHPATH}/TECHNAME}
+set PDKNAME TECHNAME
+# "sky130" is the namespace used for all devices
+set PDKNAMESPACE sky130
+puts stdout "Loading TECHNAME Device Generator Menu ..."
+
+# Initialize toolkit menus to the wrapper window
+
+global Opts
+namespace eval sky130 {}
+
+# Set the window callback
+if [catch {set Opts(callback)}] {set Opts(callback) ""}
+set Opts(callback) [subst {sky130::addtechmenu \$framename; $Opts(callback)}]
+
+# if {![info exists Opts(cmdentry)]} {set Opts(cmdentry) 1}
+
+# Set options specific to this PDK
+set Opts(hidelocked) 1
+set Opts(hidespecial) 1
+
+# Create new "tool" proc that doesn't have the netlist tool.
+proc magic::nexttool {} {
+ global Opts
+
+ # Don't attempt to switch tools while a selection drag is active
+ if {$Opts(motion) == {}} {
+ switch $Opts(tool) {
+ box { magic::tool wiring }
+ wiring { magic::tool pick }
+ default { magic::tool box }
+ }
+ }
+}
+
+# This shoule be part of sitedef. . .
+macro space magic::nexttool
+
+# Wrap the closewrapper procedure so that closing the last
+# window is equivalent to quitting.
+if {[info commands closewrapper] == "closewrapper"} {
+ rename closewrapper closewrapperonly
+ proc closewrapper { framename } {
+ if {[llength [windownames all]] <= 1} {
+ magic::quit
+ } else {
+ closewrapperonly $framename
+ }
+ }
+}
+
+# Remove maze router layers from the toolbar by locking them
+tech lock fence,magnet,rotate
+
+namespace eval sky130 {
+ namespace path {::tcl::mathop ::tcl::mathfunc}
+
+ set ruleset [dict create]
+
+ # Process DRC rules (magic style)
+
+ dict set ruleset poly_surround 0.08 ;# Poly surrounds contact
+ dict set ruleset diff_surround 0.06 ;# Diffusion surrounds contact
+ dict set ruleset gate_to_diffcont 0.145 ;# Gate to diffusion contact center
+ dict set ruleset gate_to_polycont 0.275 ;# Gate to poly contact center
+ dict set ruleset gate_extension 0.13 ;# Poly extension beyond gate
+ dict set ruleset diff_extension 0.29 ;# Diffusion extension beyond gate
+ dict set ruleset contact_size 0.17 ;# Minimum contact size
+ dict set ruleset via_size 0.17 ;# Minimum via size
+ dict set ruleset metal_surround 0.08 ;# Local interconnect overlaps contact
+ dict set ruleset sub_surround 0.18 ;# Sub/well surrounds diffusion
+ dict set ruleset diff_spacing 0.28 ;# Diffusion spacing rule
+ dict set ruleset poly_spacing 0.21 ;# Poly spacing rule
+ dict set ruleset diff_poly_space 0.075 ;# Diffusion to poly spacing rule
+ dict set ruleset diff_gate_space 0.20 ;# Diffusion to gate poly spacing rule
+ 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
+}
+
+#-----------------------------------------------------
+# magic::addtechmenu
+#-----------------------------------------------------
+
+proc sky130::addtechmenu {framename} {
+ global Winopts Opts
+
+ # Check for difference between magic 8.1.125 and earlier, and 8.1.126 and later
+ if {[catch {${framename}.titlebar cget -height}]} {
+ set layoutframe ${framename}.pane.top
+ } else {
+ set layoutframe ${framename}
+ }
+
+ # List of devices is long. Divide into two sections for active and passive deivces
+ magic::add_toolkit_menu $layoutframe "Devices 1" pdk1
+
+ magic::add_toolkit_command $layoutframe "nmos (MOSFET)" \
+ "magic::gencell sky130::nshort" pdk1
+ magic::add_toolkit_command $layoutframe "pmos (MOSFET)" \
+ "magic::gencell sky130::pshort" pdk1
+
+ magic::add_toolkit_separator $layoutframe pdk1
+ magic::add_toolkit_command $layoutframe "n-diode" \
+ "magic::gencell sky130::ndiode" pdk1
+ magic::add_toolkit_command $layoutframe "p-diode" \
+ "magic::gencell sky130::pdiode" pdk1
+
+ magic::add_toolkit_separator $layoutframe pdk1
+ magic::add_toolkit_command $layoutframe "MOS varactor" \
+ "magic::gencell sky130::xcnwvc" pdk1
+ magic::add_toolkit_separator $layoutframe pdk1
+
+ magic::add_toolkit_command $layoutframe "NPN 1x1" \
+ "magic::gencell sky130::sky130_fd_pr_rf_npn_1x1" pdk1
+ magic::add_toolkit_command $layoutframe "NPN 1x2" \
+ "magic::gencell sky130::sky130_fd_pr_rf_npn_1x2" pdk1
+ magic::add_toolkit_command $layoutframe "PNP 5x" \
+ "magic::gencell sky130::sky130_fd_pr_rf_pnp5x" pdk1
+
+ magic::add_toolkit_separator $layoutframe pdk1
+
+ magic::add_toolkit_command $layoutframe "balun" \
+ "magic::gencell sky130::balun" pdk1
+ magic::add_toolkit_command $layoutframe "inductor 011" \
+ "magic::gencell sky130::xind4_011" pdk1
+ magic::add_toolkit_command $layoutframe "inductor 02" \
+ "magic::gencell sky130::xind4_02" pdk1
+
+ magic::add_toolkit_separator $layoutframe pdk1
+
+ magic::add_toolkit_command $layoutframe "substrate contact (1.8V)" \
+ "sky130::subconn_draw" pdk1
+ magic::add_toolkit_command $layoutframe "substrate contact (5.0V)" \
+ "sky130::mvsubconn_draw" pdk1
+ magic::add_toolkit_command $layoutframe "deep n-well region" \
+ "sky130::deep_nwell_draw" pdk1
+ magic::add_toolkit_command $layoutframe "mcon" \
+ "sky130::mcon_draw" pdk1
+ magic::add_toolkit_command $layoutframe "via1" \
+ "sky130::via1_draw" pdk1
+ magic::add_toolkit_command $layoutframe "via2" \
+ "sky130::via2_draw" pdk1
+#ifdef METAL5
+ magic::add_toolkit_command $layoutframe "via3" \
+ "sky130::via3_draw" pdk1
+ magic::add_toolkit_command $layoutframe "via4" \
+ "sky130::via4_draw" pdk1
+#endif (METAL5)
+
+
+ magic::add_toolkit_menu $layoutframe "Devices 2" pdk2
+
+ magic::add_toolkit_command $layoutframe "mrdn (1.8V) - 120 Ohm/sq" \
+ "magic::gencell sky130::mrdn" pdk2
+ magic::add_toolkit_command $layoutframe "mrdp (1.8V) - 197 Ohm/sq" \
+ "magic::gencell sky130::mrdp" pdk2
+ magic::add_toolkit_command $layoutframe "mrdn_hv (5.0V) - 114 Ohm/sq" \
+ "magic::gencell sky130::mrdn_hv" pdk2
+ magic::add_toolkit_command $layoutframe "mrdp_hv (5.0V) - 191 Ohm/sq" \
+ "magic::gencell sky130::mrdp_hv" pdk2
+
+ magic::add_toolkit_command $layoutframe "mrp1 - 48.2 Ohm/sq" \
+ "magic::gencell sky130::mrp1" pdk2
+ magic::add_toolkit_command $layoutframe "xhrpoly - 319.8 Ohm/sq" \
+ "magic::gencell sky130::xhrpoly" pdk2
+ magic::add_toolkit_command $layoutframe "uhrpoly - 2000 Ohm/sq" \
+ "magic::gencell sky130::uhrpoly" pdk2
+ magic::add_toolkit_command $layoutframe "xpwres - 3050 Ohm/sq" \
+ "magic::gencell sky130::xpwres" pdk2
+ magic::add_toolkit_separator $layoutframe pdk2
+
+ magic::add_toolkit_command $layoutframe "mrl1 - 12.2 Ohm/sq" \
+ "magic::gencell sky130::mrl1" pdk2
+ magic::add_toolkit_command $layoutframe "mrm1 - 125 mOhm/sq" \
+ "magic::gencell sky130::mrm1" pdk2
+ magic::add_toolkit_command $layoutframe "mrm2 - 125 mOhm/sq" \
+ "magic::gencell sky130::mrm2" pdk2
+ magic::add_toolkit_command $layoutframe "mrm3 - 47 mOhm/sq" \
+ "magic::gencell sky130::mrm3" pdk2
+#ifdef METAL5
+ magic::add_toolkit_command $layoutframe "mrm4 - 47 mOhm/sq" \
+ "magic::gencell sky130::mrm4" pdk2
+ magic::add_toolkit_command $layoutframe "mrm5 - 29 mOhm/sq" \
+ "magic::gencell sky130::mrm5" pdk2
+#endif (METAL5)
+
+#ifdef MIM
+ magic::add_toolkit_command $layoutframe "xcmimc1 - 1fF/um^2 MiM cap" \
+ "magic::gencell sky130::xcmimc1" pdk2
+ magic::add_toolkit_command $layoutframe "xcmimc2 - 1fF/um^2 MiM cap" \
+ "magic::gencell sky130::xcmimc2" pdk2
+#endif (MIM)
+ magic::add_toolkit_separator $layoutframe pdk2
+
+ magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 li/m5 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 li/m3/m5 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 m4 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 p/m4 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield" pdk2
+ # magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 p/m5 shield" \
+ # "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 4.4x4.6 li/m3/m5 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 6.8x6.1 li/m4 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 6.8x6.1 p/m4 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 8.6x7.9 li/m3/m5 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 4.2x2 nhvnat" \
+ "magic::gencell sky130::sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 li/m3 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 m3 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield" pdk2
+ # magic::add_toolkit_command $layoutframe "vpp 1.8x1.8 li shield" \
+ # "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield" pdk2
+ # magic::add_toolkit_command $layoutframe "vpp 1.8x1.8 m3 shield" \
+ # "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 4.4x4.6 li/m3 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 4.4x4.6 m3 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 8.6x7.9 li/m3 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp 8.6x7.9 m3 shield" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield" pdk2
+ magic::add_toolkit_command $layoutframe "vpp2" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp2" pdk2
+ magic::add_toolkit_command $layoutframe "vpp2 nwell" \
+ "magic::gencell sky130::sky130_fd_pr_rf_xcmvpp2_nwell" pdk2
+
+ ${layoutframe}.titlebar.mbuttons.drc.toolmenu add command -label "DRC Routing" -command {drc style drc(routing)}
+
+ # Add command entry window by default if enabled
+ if {[info exists Opts(cmdentry)]} {
+ set Winopts(${framename},cmdentry) $Opts(cmdentry)
+ } else {
+ set Winopts(${framename},cmdentry) 0
+ }
+ if {$Winopts(${framename},cmdentry) == 1} {
+ addcommandentry $framename
+ }
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mcon_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 0.17} {
+ puts stderr "Mcon width must be at least 0.17um"
+ return
+ }
+ if {$h < 0.17} {
+ puts stderr "Mcon height must be at least 0.17um"
+ return
+ }
+ paint lic
+ box grow n 0.05um
+ box grow s 0.05um
+ paint m1
+ box grow n -0.05um
+ box grow s -0.05um
+ box grow e 0.05um
+ box grow w 0.05um
+ paint li
+ box grow e -0.05um
+ box grow w -0.05um
+}
+
+proc sky130::via1_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 0.26} {
+ puts stderr "Via1 width must be at least 0.26um"
+ return
+ }
+ if {$h < 0.26} {
+ puts stderr "Via1 height must be at least 0.26um"
+ return
+ }
+ paint via1
+ box grow n 0.05um
+ box grow s 0.05um
+ paint m2
+ box grow n -0.05um
+ box grow s -0.05um
+ box grow e 0.05um
+ box grow w 0.05um
+ paint m1
+ box grow e -0.05um
+ box grow w -0.05um
+}
+
+proc sky130::via2_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 0.28} {
+ puts stderr "Via2 width must be at least 0.28um"
+ return
+ }
+ if {$h < 0.28} {
+ puts stderr "Via2 height must be at least 0.28um"
+ return
+ }
+ paint via2
+ box grow n 0.05um
+ box grow s 0.05um
+ paint m2
+ box grow n -0.05um
+ box grow s -0.05um
+ box grow e 0.05um
+ box grow w 0.05um
+ paint m3
+ box grow e -0.05um
+ box grow w -0.05um
+}
+
+#ifdef METAL5
+proc sky130::via3_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 0.32} {
+ puts stderr "Via3 width must be at least 0.32um"
+ return
+ }
+ if {$h < 0.32} {
+ puts stderr "Via3 height must be at least 0.32um"
+ return
+ }
+ paint via3
+ box grow n 0.05um
+ box grow s 0.05um
+ paint m4
+ box grow n -0.05um
+ box grow s -0.05um
+ box grow e 0.05um
+ box grow w 0.05um
+ paint m3
+ box grow e -0.05um
+ box grow w -0.05um
+}
+
+proc sky130::via4_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 1.18} {
+ puts stderr "Via3 width must be at least 1.18um"
+ return
+ }
+ if {$h < 1.18} {
+ puts stderr "Via3 height must be at least 1.18um"
+ return
+ }
+ paint via4
+ box grow n 0.05um
+ box grow s 0.05um
+ paint m5
+ box grow n -0.05um
+ box grow s -0.05um
+ box grow e 0.05um
+ box grow w 0.05um
+ paint m4
+ box grow e -0.05um
+ box grow w -0.05um
+}
+#endif (METAL5)
+
+proc sky130::subconn_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 0.17} {
+ puts stderr "Substrate tap width must be at least 0.17um"
+ return
+ }
+ if {$h < 0.17} {
+ puts stderr "Substrate tap height must be at least 0.17um"
+ return
+ }
+ paint nsc
+ box grow c 0.1um
+ paint nsd
+ box grow c -0.1um
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mvsubconn_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 0.17} {
+ puts stderr "Substrate tap width must be at least 0.17um"
+ return
+ }
+ if {$h < 0.17} {
+ puts stderr "Substrate tap height must be at least 0.17um"
+ return
+ }
+ paint mvnsc
+ box grow c 0.1um
+ paint mvnsd
+ box grow c -0.1um
+}
+
+#----------------------------------------------------------------
+
+proc sky130::deep_nwell_draw {} {
+ set w [magic::i2u [box width]]
+ set h [magic::i2u [box height]]
+ if {$w < 3.0} {
+ puts stderr "Deep-nwell region width must be at least 3.0um"
+ return
+ }
+ if {$h < 3.0} {
+ puts stderr "Deep-nwell region height must be at least 3.0um"
+ return
+ }
+ suspendall
+ tech unlock *
+ paint dnwell
+ pushbox
+ pushbox
+ box grow c 0.4um
+ paint nwell
+ box grow c -1.43um
+ erase nwell
+ popbox
+ box grow c 0.03um
+
+ pushbox
+ box width 0
+ box grow c 0.085um
+ paint li
+ pushbox
+ box grow n -0.3um
+ box grow s -0.3um
+ paint nsc
+ popbox
+ box grow c 0.1um
+ paint nsd
+ popbox
+
+ pushbox
+ box height 0
+ box grow c 0.085um
+ paint li
+ pushbox
+ box grow e -0.3um
+ box grow w -0.3um
+ paint nsc
+ popbox
+ box grow c 0.1um
+ paint nsd
+ popbox
+
+ pushbox
+ box move n [box height]i
+ box height 0
+ box grow c 0.085um
+ paint li
+ pushbox
+ box grow e -0.3um
+ box grow w -0.3um
+ paint nsc
+ popbox
+ box grow c 0.1um
+ paint nsd
+ popbox
+
+ pushbox
+ box move e [box width]i
+ box width 0
+ box grow c 0.085um
+ paint li
+ pushbox
+ box grow n -0.3um
+ box grow s -0.3um
+ paint nsc
+ box grow c 0.1um
+ paint nsd
+ popbox
+
+ popbox
+ tech revert
+ resumeall
+}
+
+#----------------------------------------------------------------
+
+proc sky130::res_recalc {field parameters} {
+ set snake 0
+ set sterm 0.0
+ set caplen 0
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+ set val [magic::spice2float $val]
+ set l [magic::spice2float $l]
+ set w [magic::spice2float $w]
+
+ if {$snake == 0} {
+ # Straight resistor calculation
+ switch $field {
+ val { set l [expr ($val * ($w - $dw) - (2 * $term)) / $rho]
+ set w [expr ((2 * $term + $l * $rho) / $val) + $dw]
+ }
+ w { set val [expr (2 * $term + $l * $rho) / ($w - $dw)]
+ set l [expr ($val * ($w - $dw) - (2 * $term)) / $rho]
+ }
+ l { set val [expr (2 * $term + $l * $rho) / ($w - $dw)]
+ set w [expr ((2 * $term + $l * $rho) / $val) + $dw]
+ }
+ }
+ } else {
+ set term [expr $term + $sterm]
+ # Snake resistor calculation
+ switch $field {
+ val { set l [expr (($val - $rho * ($nx - 1)) * ($w - $dw) \
+ - (2 * $term) - ($rho * $caplen * ($nx - 1))) \
+ / ($rho * $nx)]
+
+ set w [expr ((2 * $term + $l * $rho * $nx \
+ + $caplen * $rho * ($nx - 1)) \
+ / ($val - $rho * ($nx - 1))) + $dw]
+ }
+ w { set val [expr $rho * ($nx - 1) + ((2 * $term) \
+ + ($rho * $l * $nx) + ($rho * $caplen * ($nx - 1))) \
+ / ($w - $dw)]
+
+ set l [expr (($val - $rho * ($nx - 1)) * ($w - $dw) \
+ - (2 * $term) - ($rho * $caplen * ($nx - 1))) \
+ / ($rho * $nx)]
+ }
+ l { set val [expr $rho * ($nx - 1) + ((2 * $term) \
+ + ($rho * $l * $nx) + ($rho * $caplen * ($nx - 1))) \
+ / ($w - $dw)]
+
+ set w [expr ((2 * $term + $l * $rho * $nx \
+ + $caplen * $rho * ($nx - 1)) \
+ / ($val - $rho * ($nx - 1))) + $dw]
+ }
+ }
+ }
+
+ set val [magic::3digitpastdecimal $val]
+ set w [magic::3digitpastdecimal $w]
+ set l [magic::3digitpastdecimal $l]
+
+ dict set parameters val $val
+ dict set parameters w $w
+ dict set parameters l $l
+
+ return $parameters
+}
+
+#----------------------------------------------------------------
+# Drawn diode routines
+#----------------------------------------------------------------
+
+proc sky130::diode_recalc {field parameters} {
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+ switch $field {
+ area { puts stdout "area changed" }
+ peri { puts stdout "perimeter changed" }
+ w { puts stdout "width changed" }
+ l { puts stdout "length changed" }
+ }
+ dict set parameters area $area
+ dict set parameters peri $peri
+ dict set parameters w $w
+ dict set parameters l $l
+}
+
+#----------------------------------------------------------------
+# diode: Conversion from SPICE netlist parameters to toolkit
+#----------------------------------------------------------------
+
+proc sky130::diode_convert {parameters} {
+ set pdkparams [dict create]
+ dict for {key value} $parameters {
+ switch -nocase $key {
+ l -
+ w -
+ peri {
+ # Length, width, and perimeter are converted to units of microns
+ set value [magic::spice2float $value]
+ # set value [expr $value * 1e6]
+ set value [magic::3digitpastdecimal $value]
+ dict set pdkparams [string tolower $key] $value
+ }
+ area {
+ # area also converted to units of microns
+ set value [magic::spice2float $value]
+ # set value [expr $value * 1e12]
+ set value [magic::3digitpastdecimal $value]
+ dict set pdkparams [string tolower $key] $value
+ }
+ m {
+ # Convert m to ny
+ dict set pdkparams ny $value
+ }
+ }
+ }
+ return $pdkparams
+}
+
+#----------------------------------------------------------------
+# diode: Interactively specifies the fixed layout parameters
+#----------------------------------------------------------------
+
+proc sky130::diode_dialog {device parameters} {
+ # Editable fields: w, l, area, perim, nx, ny
+
+ magic::add_entry area "Area (um^2)" $parameters
+ magic::add_entry peri "Perimeter (um)" $parameters
+ sky130::compute_aptot $parameters
+ magic::add_message atot "Total area (um^2)" $parameters
+ magic::add_message ptot "Total perimeter (um)" $parameters
+ magic::add_entry l "Length (um)" $parameters
+ magic::add_entry w "Width (um)" $parameters
+ magic::add_entry nx "X Repeat" $parameters
+ magic::add_entry ny "Y Repeat" $parameters
+
+ if {[dict exists $parameters compatible]} {
+ set sellist [dict get $parameters compatible]
+ magic::add_selectlist gencell "Device type" $sellist $parameters $device
+ }
+
+ if {[dict exists $parameters doverlap]} {
+ magic::add_checkbox doverlap "Overlap at end contact" $parameters
+ }
+ if {[dict exists $parameters elc]} {
+ magic::add_checkbox elc "Add left end contact" $parameters
+ }
+ if {[dict exists $parameters erc]} {
+ magic::add_checkbox erc "Add right end contact" $parameters
+ }
+ if {[dict exists $parameters etc]} {
+ magic::add_checkbox etc "Add top end contact" $parameters
+ }
+ if {[dict exists $parameters ebc]} {
+ magic::add_checkbox ebc "Add bottom end contact" $parameters
+ }
+
+ if {[dict exists $parameters guard]} {
+ magic::add_checkbox full_metal "Full metal guard ring" $parameters
+ }
+ if {[dict exists $parameters glc]} {
+ magic::add_checkbox glc "Add left guard ring contact" $parameters
+ }
+ if {[dict exists $parameters grc]} {
+ magic::add_checkbox grc "Add right guard ring contact" $parameters
+ }
+ if {[dict exists $parameters gtc]} {
+ magic::add_checkbox gtc "Add top guard ring contact" $parameters
+ }
+ if {[dict exists $parameters gbc]} {
+ magic::add_checkbox gbc "Add bottom guard ring contact" $parameters
+ }
+
+ magic::add_dependency sky130::diode_recalc $device sky130 l w area peri
+
+ # magic::add_checkbox dummy "Add dummy" $parameters
+}
+
+#----------------------------------------------------------------
+# Diode total area and perimeter computation
+#----------------------------------------------------------------
+
+proc sky130::compute_aptot {parameters} {
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+ set area [magic::spice2float $area]
+ set area [magic::3digitpastdecimal $area]
+ set peri [magic::spice2float $peri]
+ set peri [magic::3digitpastdecimal $peri]
+
+ # Compute total area
+ catch {set magic::atot_val [expr ($area * $nx * $ny)]}
+ # Compute total perimeter
+ catch {set magic::ptot_val [expr ($peri * $nx * $ny)]}
+}
+
+#----------------------------------------------------------------
+# diode: Check device parameters for out-of-bounds values
+#----------------------------------------------------------------
+
+proc sky130::diode_check {parameters} {
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set l [magic::spice2float $l]
+ set l [magic::3digitpastdecimal $l]
+ set w [magic::spice2float $w]
+ set w [magic::3digitpastdecimal $w]
+
+ set area [magic::spice2float $area]
+ set area [magic::3digitpastdecimal $area]
+ set peri [magic::spice2float $peri]
+ set peri [magic::3digitpastdecimal $peri]
+
+ if {$l == 0} {
+ # Calculate L from W and area
+ set l [expr ($area / $w)]
+ dict set parameters l [magic::float2spice $l]
+ } elseif {$w == 0} {
+ # Calculate W from L and area
+ set w [expr ($area / $l)]
+ dict set parameters w [magic::float2spice $w]
+ }
+ if {$w < $wmin} {
+ puts stderr "Diode width must be >= $wmin"
+ dict set parameters w $wmin
+ }
+ if {$l < $lmin} {
+ puts stderr "Diode length must be >= $lmin"
+ dict set parameters l $lmin
+ }
+ # Calculate area and perimeter from L and W
+ set area [expr ($l * $w)]
+ dict set parameters area [magic::float2spice $area]
+ set peri [expr (2 * ($l + $w))]
+ dict set parameters peri [magic::float2spice $peri]
+ sky130::compute_aptot $parameters
+
+ return $parameters
+}
+
+#------------------------------------------------------------------
+# NOTE: ndiode_lvt, ndiode_native, pdiode_lvt, and pdiode_hvt are
+# all considered parasitic diodes. They may be generated by
+# invoking the build procedure on the command line. To enable them
+# in the PDK, add them to the appropriate compatible {} list.
+#------------------------------------------------------------------
+
+proc sky130::ndiode_defaults {} {
+ return {w 0.45 l 0.45 area 0.2025 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 doverlap 0 \
+ compatible {ndiode ndiode_h} full_metal 1}
+}
+
+proc sky130::ndiode_lvt_defaults {} {
+ return {w 0.45 l 0.45 area 0.2025 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 doverlap 0 \
+ full_metal 1}
+}
+
+proc sky130::pdiode_defaults {} {
+ return {w 0.45 l 0.45 area 0.2025 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 \
+ glc 1 grc 1 gtc 1 gbc 1 doverlap 0 \
+ compatible {pdiode pdiode_h} full_metal 1}
+}
+
+proc sky130::pdiode_lvt_defaults {} {
+ return {w 0.45 l 0.45 area 0.2025 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 \
+ glc 1 grc 1 gtc 1 gbc 1 doverlap 0 \
+ full_metal 1}
+}
+
+proc sky130::pdiode_hvt_defaults {} {
+ return {w 0.45 l 0.45 area 0.2025 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 \
+ glc 1 grc 1 gtc 1 gbc 1 doverlap 0 \
+ full_metal 1}
+}
+
+proc sky130::ndiode_h_defaults {} {
+ return {w 0.45 l 0.45 area 0.2024 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 doverlap 0 \
+ compatible {ndiode ndiode_h} full_metal 1}
+}
+
+proc sky130::ndiode_native_defaults {} {
+ return {w 0.45 l 0.45 area 0.2024 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 doverlap 0 \
+ full_metal 1}
+}
+
+proc sky130::pdiode_h_defaults {} {
+ return {w 0.45 l 0.45 area 0.2024 peri 1.8 \
+ nx 1 ny 1 dummy 0 lmin 0.45 wmin 0.45 \
+ elc 1 erc 1 etc 1 ebc 1 \
+ glc 1 grc 1 gtc 1 gbc 1 doverlap 0 \
+ compatible {pdiode pdiode_h} full_metal 1}
+}
+
+#----------------------------------------------------------------
+
+proc sky130::ndiode_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::ndiode_lvt_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::pdiode_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::pdiode_lvt_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::pdiode_hvt_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::ndiode_h_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::ndiode_native_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+proc sky130::pdiode_h_convert {parameters} {
+ return [sky130::diode_convert $parameters]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::ndiode_dialog {parameters} {
+ sky130::diode_dialog ndiode $parameters
+}
+
+proc sky130::ndiode_lvt_dialog {parameters} {
+ sky130::diode_dialog ndiode_lvt $parameters
+}
+
+proc sky130::pdiode_dialog {parameters} {
+ sky130::diode_dialog pdiode $parameters
+}
+
+proc sky130::pdiode_lvt_dialog {parameters} {
+ sky130::diode_dialog pdiode_lvt $parameters
+}
+
+proc sky130::pdiode_hvt_dialog {parameters} {
+ sky130::diode_dialog pdiode_hvt $parameters
+}
+
+proc sky130::ndiode_h_dialog {parameters} {
+ sky130::diode_dialog ndiode_h $parameters
+}
+
+proc sky130::ndiode_native_dialog {parameters} {
+ sky130::diode_dialog ndiode_native $parameters
+}
+
+proc sky130::pdiode_h_dialog {parameters} {
+ sky130::diode_dialog pdiode_h $parameters
+}
+
+#----------------------------------------------------------------
+
+proc sky130::ndiode_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::ndiode_lvt_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::pdiode_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::pdiode_lvt_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::pdiode_hvt_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::ndiode_h_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::ndiode_native_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+proc sky130::pdiode_h_check {parameters} {
+ sky130::diode_check $parameters
+}
+
+#----------------------------------------------------------------
+# Diode: Draw a single device
+#----------------------------------------------------------------
+
+proc sky130::diode_device {parameters} {
+ # Epsilon for avoiding round-off errors
+ set eps 0.0005
+
+ # Set local default values if they are not in parameters
+ set dev_surround 0
+ set dev_sub_type ""
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # If there is no end_sub_surround, set it to sub_surround
+ if {![dict exists $parameters end_sub_surround]} {
+ set end_sub_surround $sub_surround
+ }
+
+ # Draw the device
+ pushbox
+ box size 0 0
+
+ set hw [/ $w 2.0]
+ set hl [/ $l 2.0]
+
+ # Calculate ring size (measured to contact center)
+ set gx [+ $w [* 2.0 [+ $dev_spacing $dev_surround]] $contact_size]
+ set gy [+ $l [* 2.0 [+ $dev_spacing $dev_surround]] $contact_size]
+
+ # Draw the ring first, because diode may occupy well/substrate plane
+ set guardparams $parameters
+ dict set guardparams plus_diff_type $end_type
+ dict set guardparams plus_contact_type $end_contact_type
+ dict set guardparams diff_surround $end_surround
+ dict set guardparams sub_type $end_sub_type
+ dict set guardparams sub_surround $sub_surround
+ dict set guardparams guard_sub_surround $end_sub_surround
+ dict set guardparams glc $elc
+ dict set guardparams grc $erc
+ dict set guardparams gtc $etc
+ dict set guardparams gbc $ebc
+ set cext [sky130::guard_ring $gx $gy $guardparams]
+
+ pushbox
+ box grow n ${hl}um
+ box grow s ${hl}um
+ box grow e ${hw}um
+ box grow w ${hw}um
+ paint ${dev_type}
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+
+ if {$dev_sub_type != ""} {
+ box grow n ${sub_surround}um
+ box grow s ${sub_surround}um
+ box grow e ${sub_surround}um
+ box grow w ${sub_surround}um
+ paint ${dev_sub_type}
+ }
+ popbox
+
+ if {${w} < ${l}} {
+ set orient vert
+ } else {
+ set orient horz
+ }
+
+ # Reduce width by surround amount
+ set w [- $w [* ${dev_surround} 2.0]]
+ set l [- $l [* ${dev_surround} 2.0]]
+
+ set cext [sky130::unionbox $cext [sky130::draw_contact ${w} ${l} \
+ ${dev_surround} ${metal_surround} ${contact_size} \
+ ${dev_type} ${dev_contact_type} li ${orient}]]
+
+ popbox
+ return $cext
+}
+
+#----------------------------------------------------------------
+# Diode: Draw the tiled device
+#----------------------------------------------------------------
+
+proc sky130::diode_draw {parameters} {
+ tech unlock *
+
+ # Set defaults if they are not in parameters
+ set doverlap 0 ;# overlap diodes at contacts
+ set guard 0 ;# draw a guard ring
+ set prohibit_overlap false ;# don't prohibit overlaps
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set w [magic::spice2float $w]
+ set l [magic::spice2float $l]
+
+ pushbox
+ box values 0 0 0 0
+
+ # Determine the base device dimensions by drawing one device
+ # while all layers are locked (nothing drawn). This allows the
+ # base drawing routine to do complicated geometry without having
+ # to duplicate it here with calculations.
+
+ tech lock *
+ set bbox [sky130::diode_device $parameters]
+ # puts stdout "Diagnostic: Device bounding box e $bbox (um)"
+ tech unlock *
+
+ set fw [- [lindex $bbox 2] [lindex $bbox 0]]
+ set fh [- [lindex $bbox 3] [lindex $bbox 1]]
+ set lw [+ [lindex $bbox 2] [lindex $bbox 0]]
+ set lh [+ [lindex $bbox 3] [lindex $bbox 1]]
+
+ # If prohibit_overlap is true, then end overlapping is prohibited when
+ # nx or ny is > 1 to prevent DRC errors (typically from well spacing rule)
+ if {$prohibit_overlap == true} {
+ if {($nx > 1) || ($ny > 1)} {
+ set doverlap 0
+ }
+ }
+
+ # Determine tile width and height (depends on overlap)
+
+ if {$doverlap == 0} {
+ set dx [+ $fw $end_spacing]
+ set dy [+ $fh $end_spacing]
+ } else {
+ # overlap contact
+ set dx [- $fw [+ [* 2.0 $sub_surround] [* 2.0 $end_surround] $contact_size]]
+ set dy [- $fh [+ [* 2.0 $sub_surround] [* 2.0 $end_surround] $contact_size]]
+ }
+
+ # Determine core width and height
+ set corex [+ [* [- $nx 1] $dx] $fw]
+ set corey [+ [* [- $ny 1] $dy] $fh]
+ set corellx [/ [+ [- $corex $fw] $lw] 2.0]
+ set corelly [/ [+ [- $corey $fh] $lh] 2.0]
+
+ if {$guard != 0} {
+ # Calculate guard ring size (measured to contact center)
+ set gx [+ $corex [* 2.0 [+ $diff_spacing $diff_surround]] $contact_size]
+ set gy [+ $corey [* 2.0 [+ $diff_spacing $diff_surround]] $contact_size]
+
+ # Draw the guard ring first, because diode may occupy well/substrate plane
+ sky130::guard_ring $gx $gy $parameters
+ }
+
+ pushbox
+ box move w ${corellx}um
+ box move s ${corelly}um
+ if {($nx > 1) || ($ny > 1)} {
+ pushbox
+ set hfw [/ $fw 2.0]
+ set hfh [/ $fh 2.0]
+ box move w ${hfw}um
+ box move s ${hfh}um
+ box size ${corex}um ${corey}um
+ paint $end_sub_type
+ popbox
+ }
+ for {set xp 0} {$xp < $nx} {incr xp} {
+ pushbox
+ for {set yp 0} {$yp < $ny} {incr yp} {
+ sky130::diode_device $parameters
+ box move n ${dy}um
+ }
+ popbox
+ box move e ${dx}um
+ }
+ popbox
+ popbox
+
+ tech revert
+}
+
+#----------------------------------------------------------------
+
+proc sky130::ndiode_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type ndiode \
+ dev_contact_type ndic \
+ end_type psd \
+ end_contact_type psc \
+ end_sub_type psub \
+ dev_spacing ${diff_spacing} \
+ dev_surround ${diff_surround} \
+ end_spacing ${diff_spacing} \
+ end_surround 0 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+# NOTE: Use ppd instead of psd so that there is additional
+# diffusion around the contact, allowing more space for the
+# implant (likewise pdiode_lvt and pdiode_hvt).
+
+proc sky130::ndiode_lvt_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type ndiodelvt \
+ dev_contact_type ndilvtc \
+ end_type ppd \
+ end_contact_type psc \
+ end_sub_type psub \
+ dev_spacing ${diff_spacing} \
+ dev_surround ${diff_surround} \
+ end_spacing ${diff_spacing} \
+ end_surround ${diff_surround} \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::pdiode_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type pdiode \
+ guard 1 \
+ dev_contact_type pdic \
+ end_type nsd \
+ end_contact_type nsc \
+ end_sub_type nwell \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ sub_type psub \
+ dev_spacing ${diff_spacing} \
+ dev_surround ${diff_surround} \
+ end_spacing ${diff_spacing} \
+ end_surround 0 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::pdiode_lvt_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type pdiodelvt \
+ guard 1 \
+ dev_contact_type pdilvtc \
+ end_type nnd \
+ end_contact_type nsc \
+ end_sub_type nwell \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ sub_type psub \
+ dev_spacing ${diff_spacing} \
+ dev_surround ${diff_surround} \
+ end_spacing ${diff_spacing} \
+ end_surround ${diff_surround} \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::pdiode_hvt_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type pdiodehvt \
+ guard 1 \
+ dev_contact_type pdihvtc \
+ end_type nnd \
+ end_contact_type nsc \
+ end_sub_type nwell \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ sub_type psub \
+ dev_spacing ${diff_spacing} \
+ dev_surround ${diff_surround} \
+ end_spacing ${diff_spacing} \
+ end_surround ${diff_surround} \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::ndiode_h_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type mvndiode \
+ dev_contact_type mvndic \
+ end_type mvpsd \
+ end_contact_type mvpsc \
+ end_sub_type psub \
+ diff_spacing 0.37 \
+ dev_spacing 0.39 \
+ dev_surround ${diff_surround} \
+ end_spacing 0.36 \
+ end_surround ${diff_surround} \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+
+proc sky130::ndiode_native_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ dev_type nndiode \
+ dev_contact_type nndic \
+ end_type mvpsd \
+ end_contact_type mvpsc \
+ end_sub_type psub \
+ dev_spacing 0.37 \
+ dev_surround ${diff_surround} \
+ end_spacing 0.30 \
+ end_surround ${diff_surround} \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+
+#----------------------------------------------------------------
+
+proc sky130::pdiode_h_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 1 \
+ dev_type mvpdiode \
+ dev_contact_type mvpdic \
+ end_type mvnsd \
+ end_contact_type mvnsc \
+ end_sub_type nwell \
+ plus_diff_type mvpsd \
+ plus_contact_type mvpsc \
+ sub_type psub \
+ diff_spacing 0.58 \
+ dev_spacing 0.37 \
+ dev_surround ${diff_surround} \
+ end_spacing 0.30 \
+ end_sub_surround 0.33 \
+ end_surround ${diff_surround} \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::diode_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+# Drawn capactitor routines
+# NOTE: Work in progress. These values need to be corrected.
+#----------------------------------------------------------------
+
+#ifdef MIM
+proc sky130::xcmimc1_defaults {} {
+ return {w 2.00 l 2.00 val 4.0 carea 1.00 cperi 0.17 \
+ nx 1 ny 1 dummy 0 square 0 lmin 2.00 wmin 2.00 \
+ lmax 30.0 wmax 30.0 dc 0 bconnect 1 tconnect 1}
+}
+proc sky130::xcmimc2_defaults {} {
+ return {w 2.00 l 2.00 val 4.0 carea 1.00 cperi 0.17 \
+ nx 1 ny 1 dummy 0 square 0 lmin 2.00 wmin 2.00 \
+ lmax 30.0 wmax 30.0 dc 0 bconnect 1 tconnect 1}
+}
+#endif (MIM)
+
+
+#----------------------------------------------------------------
+# Recalculate capacitor values from GUI entries.
+# Recomputes W/L and Value as long as 2 of them are present
+# (To be completed)
+#----------------------------------------------------------------
+
+proc sky130::cap_recalc {field parameters} {
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+ switch $field {
+ val { puts stdout "value changed" }
+ w { puts stdout "width changed" }
+ l { puts stdout "length changed" }
+ }
+ dict set parameters val $val
+ dict set parameters w $w
+ dict set parameters l $l
+}
+
+#----------------------------------------------------------------
+# Capacitor defaults:
+#----------------------------------------------------------------
+# w Width of drawn cap
+# l Length of drawn cap
+# nx Number of devices in X
+# ny Number of devices in Y
+# val Default cap value
+# carea Area
+# cperi Perimeter
+# dummy Add dummy cap
+# square Make square capacitor
+#
+# (not user-editable)
+#
+# wmin Minimum allowed width
+# lmin Minimum allowed length
+# dc Area to remove to calculated area
+#----------------------------------------------------------------
+
+#----------------------------------------------------------------
+# capacitor: Conversion from SPICE netlist parameters to toolkit
+#----------------------------------------------------------------
+
+proc sky130::cap_convert {parameters} {
+ set pdkparams [dict create]
+ dict for {key value} $parameters {
+ switch -nocase $key {
+ l -
+ w {
+ # Length and width are converted to units of microns
+ set value [magic::spice2float $value]
+ # set value [expr $value * 1e6]
+ set value [magic::3digitpastdecimal $value]
+ dict set pdkparams [string tolower $key] $value
+ }
+ m {
+ # Convert m to ny
+ dict set pdkparams ny $value
+ }
+ }
+ }
+ return $pdkparams
+}
+
+#ifdef MIM
+proc sky130::xcmimc1_convert {parameters} {
+ return [cap_convert $parameters]
+}
+proc sky130::xcmimc2_convert {parameters} {
+ return [cap_convert $parameters]
+}
+#endif (MIM)
+
+#----------------------------------------------------------------
+# capacitor: Interactively specifies the fixed layout parameters
+#----------------------------------------------------------------
+
+proc sky130::cap_dialog {device parameters} {
+ # Editable fields: w, l, nx, ny, val
+ # Checked fields: square, dummy
+
+ magic::add_entry val "Value (fF)" $parameters
+ sky130::compute_ctot $parameters
+ magic::add_message ctot "Total capacitance (pF)" $parameters
+ magic::add_entry l "Length (um)" $parameters
+ magic::add_entry w "Width (um)" $parameters
+ magic::add_entry nx "X Repeat" $parameters
+ magic::add_entry ny "Y Repeat" $parameters
+
+ if {[dict exists $parameters square]} {
+ magic::add_checkbox square "Square capacitor" $parameters
+ }
+ if {[dict exists $parameters bconnect]} {
+ magic::add_checkbox bconnect "Connect bottom plates in array" $parameters
+ }
+ if {[dict exists $parameters tconnect]} {
+ magic::add_checkbox tconnect "Connect top plates in array" $parameters
+ }
+ if {[dict exists $parameters guard]} {
+ magic::add_checkbox guard "Add guard ring" $parameters
+ }
+
+ magic::add_dependency sky130::cap_recalc $device sky130 l w val
+
+ # magic::add_checkbox dummy "Add dummy" $parameters
+}
+
+#ifdef MIM
+proc sky130::xcmimc1_dialog {parameters} {
+ sky130::cap_dialog xcmimc1 $parameters
+}
+proc sky130::xcmimc2_dialog {parameters} {
+ sky130::cap_dialog xcmimc2 $parameters
+}
+#endif (MIM)
+
+#----------------------------------------------------------------
+# Capacitor total capacitance computation
+#----------------------------------------------------------------
+
+proc sky130::compute_ctot {parameters} {
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+ set val [magic::spice2float $val]
+ set val [magic::3digitpastdecimal $val]
+
+ # Compute total capacitance (and convert fF to pF)
+ catch {set magic::ctot_val [expr (0.001 * $val * $nx * $ny)]}
+}
+
+#----------------------------------------------------------------
+# Capacitor: Draw a single device
+#----------------------------------------------------------------
+
+proc sky130::cap_device {parameters} {
+ # Epsilon for avoiding round-off errors
+ set eps 0.0005
+
+ # Set local default values if they are not in parameters
+ set cap_surround 0
+ set bot_surround 0
+ set top_surround 0
+ set bconnect 0 ;# bottom plates are connected in array
+ set cap_spacing 0 ;# cap spacing in array
+ set top_metal_space 0 ;# top metal spacing (if larger than cap spacing)
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ if {![dict exists $parameters top_metal_space]} {
+ set top_metal_space $metal_spacing
+ }
+
+ # Draw the device
+ pushbox
+ box size 0 0
+
+ pushbox
+ set hw [/ $w 2.0]
+ set hl [/ $l 2.0]
+ box grow e ${hw}um
+ box grow w ${hw}um
+ box grow n ${hl}um
+ box grow s ${hl}um
+ paint ${cap_type}
+ pushbox
+ box grow n -${cap_surround}um
+ box grow s -${cap_surround}um
+ box grow e -${cap_surround}um
+ box grow w -${cap_surround}um
+ paint ${cap_contact_type}
+ pushbox
+ box grow n ${top_surround}um
+ box grow s ${top_surround}um
+ box grow e ${top_surround}um
+ box grow w ${top_surround}um
+ paint ${top_type}
+ set cext [sky130::getbox]
+ popbox
+ popbox
+ pushbox
+ box grow n ${bot_surround}um
+ box grow s ${bot_surround}um
+ box grow e ${bot_surround}um
+ box grow w ${bot_surround}um
+
+ paint ${bot_type}
+ # Create boundary using properties
+ property FIXED_BBOX [box values]
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+
+ # Extend bottom metal under contact to right
+ box grow e ${end_spacing}um
+ set chw [/ ${contact_size} 2.0]
+ box grow e ${chw}um
+ box grow e ${end_surround}um
+ paint ${bot_type}
+
+ popbox
+ popbox
+
+ # Draw contact to right. Reduce contact extent if devices are not
+ # wired together and the top metal spacing rule limits the distance
+ set lcont $l
+ if {($bconnect == 0) && ($ny > 1)} {
+ if {$cap_spacing < $top_metal_space} {
+ set cspace [- $top_metal_space $cap_spacing]
+ set lcont [- $l $cspace]
+ }
+ }
+
+ pushbox
+ box move e ${hw}um
+ box move e ${bot_surround}um
+ box move e ${end_spacing}um
+ set cl [- [+ ${lcont} [* ${bot_surround} 2.0]] [* ${end_surround} 2.0]]
+ set cl [- ${cl} ${metal_surround}] ;# see below
+ set cext [sky130::unionbox $cext [sky130::draw_contact 0 ${cl} \
+ ${end_surround} ${metal_surround} ${contact_size} \
+ ${bot_type} ${top_contact_type} ${top_type} full]]
+ popbox
+ popbox
+
+ return $cext
+
+ # cl shrinks top and bottom to accomodate larger bottom metal
+ # surround rule for contacts near a MiM cap. This should be its
+ # own variable, but metal_surround is sufficient.
+}
+
+#----------------------------------------------------------------
+# Metal plate sandwich capacitor: Draw a single device
+#----------------------------------------------------------------
+
+proc sky130::sandwich_cap_device {parameters} {
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ pushbox
+ box size 0 0
+
+ set hw [/ $w 2.0]
+ set hl [/ $l 2.0]
+
+ set cw [- [* $hw [/ 2.0 3]] [* $cont_surround 2.0]]
+ set cl [- [* $hl [/ 2.0 3]] [* $cont_surround 2.0]]
+
+ # plate capacitor defines layers p0, p1, etc.
+ for {set i 0} {$i < 20} {incr i} {
+ if {[catch {set layer [subst \$p${i}_type]}]} {break} ;# no more layers defined
+ pushbox
+ box grow e ${hw}um
+ box grow w ${hw}um
+ box grow n ${hl}um
+ box grow s ${hl}um
+ if {![catch {set shrink [subst \$p${i}_shrink]}]} {
+ box grow e -${shrink}um
+ box grow w -${shrink}um
+ box grow n -${shrink}um
+ box grow s -${shrink}um
+ set cutout_spacing [+ [* ${shrink} 2.0] [/ $via_size 2.0] $cont_surround]
+ } else {
+ set cutout_spacing 0
+ }
+
+ paint ${layer}
+
+ if {$i == 1} {
+ # Note that cap_type geometry is coincident with p1_type.
+ # Typically, this will define a layer that outputs as both
+ # poly and a capacitor definition layer.
+ if {[dict exists $parameters cap_type]} {
+ paint $cap_type
+ }
+ }
+ popbox
+
+ # Even layers connect at corners, odd layers connect at sides.
+ # Even layers cut out the sides, odd layers cut out the corners.
+ # Layer zero has no side contacts or cutout.
+
+ if {[% $i 2] == 0} {
+ set cornercmd paint
+ set cornersize $cutout_spacing
+ set sidecmd erase
+ set nssidelong [+ $cutout_spacing [/ $hw 3.0]]
+ set ewsidelong [+ $cutout_spacing [/ $hl 3.0]]
+ set sideshort $cutout_spacing
+ } else {
+ set cornercmd erase
+ set cornersize $cutout_spacing
+ set sidecmd paint
+ set nssidelong [/ $hw 3.0]
+ set ewsidelong [/ $hl 3.0]
+ set sideshort $cutout_spacing
+ }
+
+ if {$i > 0} {
+ pushbox
+ box move e ${hw}um
+ box grow n ${ewsidelong}um
+ box grow s ${ewsidelong}um
+ box grow w ${sideshort}um
+ ${sidecmd} ${layer}
+ popbox
+ pushbox
+ box move n ${hl}um
+ box grow e ${nssidelong}um
+ box grow w ${nssidelong}um
+ box grow s ${sideshort}um
+ ${sidecmd} ${layer}
+ popbox
+ pushbox
+ box move w ${hw}um
+ box grow n ${ewsidelong}um
+ box grow s ${ewsidelong}um
+ box grow e ${sideshort}um
+ ${sidecmd} ${layer}
+ popbox
+ pushbox
+ box move s ${hl}um
+ box grow e ${nssidelong}um
+ box grow w ${nssidelong}um
+ box grow n ${sideshort}um
+ ${sidecmd} ${layer}
+ popbox
+
+ pushbox
+ box move n ${hl}um
+ box move e ${hw}um
+ box grow s ${cornersize}um
+ box grow w ${cornersize}um
+ ${cornercmd} ${layer}
+ popbox
+ pushbox
+ box move n ${hl}um
+ box move w ${hw}um
+ box grow s ${cornersize}um
+ box grow e ${cornersize}um
+ ${cornercmd} ${layer}
+ popbox
+ pushbox
+ box move s ${hl}um
+ box move e ${hw}um
+ box grow n ${cornersize}um
+ box grow w ${cornersize}um
+ ${cornercmd} ${layer}
+ popbox
+ pushbox
+ box move s ${hl}um
+ box move w ${hw}um
+ box grow n ${cornersize}um
+ box grow e ${cornersize}um
+ ${cornercmd} ${layer}
+ popbox
+ }
+ }
+
+ # Draw contacts after all layers have been drawn, so that erasing
+ # layers does not affect the contacts.
+
+ for {set i 0} {$i < 20} {incr i} {
+ if {![catch {set contact [subst \$p${i}_contact_type]}]} {
+ set layer [subst \$p${i}_type]
+ set j [+ $i 1]
+ set toplayer [subst \$p${j}_type]
+
+ # Draw corner contacts
+ pushbox
+ box move e ${hw}um
+ box move n ${hl}um
+ sky130::draw_contact 0 0 \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ pushbox
+ box move w ${hw}um
+ box move n ${hl}um
+ sky130::draw_contact 0 0 \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ pushbox
+ box move e ${hw}um
+ box move s ${hl}um
+ sky130::draw_contact 0 0 \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ pushbox
+ box move w ${hw}um
+ box move s ${hl}um
+ sky130::draw_contact 0 0 \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+
+ # Draw side contacts (except on poly)
+ if {$i > 0} {
+ pushbox
+ box move w ${hw}um
+ sky130::draw_contact 0 ${cl} \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ pushbox
+ box move e ${hw}um
+ sky130::draw_contact 0 ${cl} \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ pushbox
+ box move n ${hl}um
+ sky130::draw_contact ${cw} 0 \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ pushbox
+ box move s ${hl}um
+ sky130::draw_contact ${cw} 0 \
+ ${cont_surround} ${cont_surround} ${via_size} \
+ ${layer} ${contact} ${toplayer} full
+ popbox
+ }
+ } else {
+ break
+ }
+ }
+
+ popbox
+ # Bounding box is the same as the device length and width
+ set cext [list -$hw -$hl $hw $hl]
+ return $cext
+}
+
+#----------------------------------------------------------------
+# Capacitor: Draw the tiled device
+#----------------------------------------------------------------
+
+proc sky130::cap_draw {parameters} {
+ tech unlock *
+ set savesnap [snap]
+ snap internal
+
+ # Set defaults if they are not in parameters
+ set coverlap 0 ;# overlap capacitors at contacts
+ set guard 0 ;# draw a guard ring
+ set sandwich 0 ;# this is not a plate sandwich capacitor
+ set cap_spacing 0 ;# abutted caps if spacing is zero
+ set cap_diff_spacing 0
+ set wide_cap_spacing 0 ;# additional spacing for wide metal rule
+ set wide_cap_width 0
+ set end_spacing 0
+ set end_surround 0
+ set bot_surround 0
+ set top_metal_width 0
+ set bconnect 0 ;# connect bottom plates in array
+ set tconnect 0 ;# connect top plates in array
+ set top_type ""
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set w [magic::spice2float $w]
+ set l [magic::spice2float $l]
+
+ pushbox
+ box values 0 0 0 0
+
+ # Determine the base device dimensions by drawing one device
+ # while all layers are locked (nothing drawn). This allows the
+ # base drawing routine to do complicated geometry without having
+ # to duplicate it here with calculations.
+
+ tech lock *
+ if {$sandwich == 1} {
+ set bbox [sky130::sandwich_cap_device $parameters]
+ } else {
+ set bbox [sky130::cap_device $parameters]
+ }
+ # puts stdout "Diagnostic: Device bounding box e $bbox (um)"
+ tech unlock *
+
+ set fw [- [lindex $bbox 2] [lindex $bbox 0]]
+ set fh [- [lindex $bbox 3] [lindex $bbox 1]]
+ set lw [+ [lindex $bbox 2] [lindex $bbox 0]]
+ set lh [+ [lindex $bbox 3] [lindex $bbox 1]]
+
+ set dwide 0
+ if {($fw >= $wide_cap_width) && ($fh >= $wide_cap_width)} {
+ set dwide $wide_cap_spacing
+ }
+
+ # Determine tile width and height (depends on overlap)
+ if {$coverlap == 0} {
+ set dy [+ $fh $cap_spacing $dwide]
+ } else {
+ # overlap at end contact
+ set dy [- $fh [+ $end_surround $end_surround $contact_size]]
+ }
+ # Contact is placed on right so spacing is determined by end_spacing.
+ set dx [+ $fw $end_spacing $dwide]
+
+ # Determine core width and height
+ set corex [+ [* [- $nx 1] $dx] $fw]
+ set corey [+ [* [- $ny 1] $dy] $fh]
+ set corellx [/ [+ [- $corex $fw] $lw] 2.0]
+ set corelly [/ [+ [- $corey $fh] $lh] 2.0]
+
+ if {$guard != 0} {
+ # Calculate guard ring size (measured to contact center)
+ set gx [+ $corex [* 2.0 [+ $cap_diff_spacing $diff_surround]] $contact_size]
+ set gy [+ $corey [* 2.0 [+ $end_spacing $diff_surround]] $contact_size]
+
+ # Draw the guard ring first.
+ sky130::guard_ring $gx $gy $parameters
+ }
+
+ set twidth [+ ${contact_size} ${end_surround} ${end_surround}]
+ if {${twidth} < ${top_metal_width}} {
+ set twidth ${top_metal_width}
+ }
+ set hmw [/ $twidth 2.0]
+ set hdy [/ $dy 2.0]
+ set cdx [+ [/ ${w} 2.0] ${bot_surround} ${end_spacing}]
+
+ pushbox
+ box move w ${corellx}um
+ box move s ${corelly}um
+ for {set xp 0} {$xp < $nx} {incr xp} {
+ pushbox
+ for {set yp 0} {$yp < $ny} {incr yp} {
+ if {$sandwich == 1} {
+ sky130::sandwich_cap_device $parameters
+ } else {
+ sky130::cap_device $parameters
+ }
+ if {$ny > 1} {
+ pushbox
+ box grow e ${hmw}um
+ box grow w ${hmw}um
+ box grow n ${hdy}um
+ box grow s ${hdy}um
+ if {($top_type != "") && ($tconnect == 1)} {
+ paint ${top_type}
+ }
+ if {($top_type != "") && ($bconnect == 1)} {
+ box move e ${cdx}um
+ paint ${top_type}
+ }
+ popbox
+ }
+ box move n ${dy}um
+ }
+ popbox
+ box move e ${dx}um
+ }
+ popbox
+ popbox
+
+ snap $savesnap
+ tech revert
+}
+
+#----------------------------------------------------------------
+
+#ifdef MIM
+proc sky130::xcmimc1_draw {parameters} {
+ set newdict [dict create \
+ top_type m4 \
+ top_contact_type via3 \
+ cap_type mimcap \
+ cap_contact_type mimcc \
+ bot_type m3 \
+ bot_surround 0.5 \
+ cap_spacing 0.5 \
+ cap_surround 0.2 \
+ top_surround 0.005 \
+ end_surround 0.1 \
+ end_spacing 0.60 \
+ contact_size 0.32 \
+ metal_surround 0.08 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::cap_draw $drawdict]
+}
+
+proc sky130::xcmimc2_draw {parameters} {
+ set newdict [dict create \
+ top_type m5 \
+ top_contact_type via4 \
+ cap_type mimcap2 \
+ cap_contact_type mim2cc \
+ bot_type m4 \
+ bot_surround 0.5 \
+ cap_spacing 0.5 \
+ cap_surround 0.2 \
+ top_surround 0.12 \
+ end_surround 0.1 \
+ end_spacing 2.10 \
+ contact_size 1.18 \
+ metal_surround 0.21 \
+ top_metal_width 1.6 \
+ top_metal_space 1.7 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::cap_draw $drawdict]
+}
+
+#endif (MIM)
+
+#----------------------------------------------------------------
+# capacitor: Check device parameters for out-of-bounds values
+#----------------------------------------------------------------
+
+proc sky130::cap_check {parameters} {
+ # In case wmax and/or lmax are undefined
+ set lmax 0
+ set wmax 0
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set l [magic::spice2float $l]
+ set l [magic::3digitpastdecimal $l]
+ set w [magic::spice2float $w]
+ set w [magic::3digitpastdecimal $w]
+
+ set val [magic::spice2float $val]
+ set carea [magic::spice2float $carea]
+ set cperi [magic::spice2float $cperi]
+ set dc [magic::spice2float $dc]
+
+ if {$square == 1} {
+ # Calculate L and W from value
+ set a $carea
+ set b [expr $cperi * 4]
+ set c [expr -4 * $dc - $val]
+ set l [expr ((-$b + sqrt($b * $b - (4 * $a * $c))) / (2 * $a))]
+ dict set parameters l [magic::float2spice $l]
+ set w $l
+ dict set parameters w [magic::float2spice $w]
+ } elseif {$l == 0} {
+ # Calculate L from W and value
+ set l [expr (($val + 4 * $dc - 2 * $w * $cperi) / ($w * $carea + 2 * $cperi))]
+ dict set parameters l [magic::float2spice $l]
+ } elseif {$w == 0} {
+ # Calculate W from L and value
+ set w [expr (($val + 4 * $dc - 2 * $l * $cperi) / ($l * $carea + 2 * $cperi))]
+ dict set parameters w [magic::float2spice $w]
+ }
+ if {$w < $wmin} {
+ puts stderr "Capacitor width must be >= $wmin"
+ dict set parameters w $wmin
+ set w $wmin
+ }
+ if {$l < $lmin} {
+ puts stderr "Capacitor length must be >= $lmin"
+ dict set parameters l $lmin
+ set l $lmin
+ }
+ if {($wmax > 0) && ($w > $wmax)} {
+ puts stderr "Capacitor width must be <= $wmax"
+ dict set parameters w $wmax
+ set w $wmax
+ }
+ if {($lmax > 0) && ($l > $lmax)} {
+ puts stderr "Capacitor length must be <= $lmax"
+ dict set parameters l $lmax
+ set l $lmax
+ }
+ # Calculate value from L and W
+ set cval [expr ($l * $w * $carea + 2 * ($l + $w) * $cperi - 4 * $dc)]
+ dict set parameters val [magic::float2spice $cval]
+ sky130::compute_ctot $parameters
+
+ return $parameters
+}
+
+#ifdef MIM
+proc sky130::xcmimc1_check {parameters} {
+ return [sky130::cap_check $parameters]
+}
+proc sky130::xcmimc2_check {parameters} {
+ return [sky130::cap_check $parameters]
+}
+#endif (MIM)
+
+#----------------------------------------------------------------
+# Drawn resistors
+#----------------------------------------------------------------
+
+#----------------------------------------------------------------
+# Resistor defaults:
+#----------------------------------------------------------------
+# User editable values:
+#
+# val Resistor value in ohms
+# w Width
+# l Length
+# t Number of turns
+# m Number devices in Y
+# nx Number devices in X
+# snake Use snake geometry (if not present, snake geometry not allowed)
+# dummy Flag to mark addition of dummy resistor
+#
+# Non-user editable values:
+#
+# wmin Minimum allowed width
+# lmin Minimum allowed length
+# rho Resistance in ohms per square
+# dw Delta width
+# term Resistance per terminal
+# sterm Additional resistance per terminal for snake geometry
+#----------------------------------------------------------------
+
+#----------------------------------------------------------------
+# xpwres: Specify all user-editable default values and those
+# needed by xpwres_check
+# NOTE: Work in progress. These values need to be corrected.
+#----------------------------------------------------------------
+
+proc sky130::xpwres_defaults {} {
+ return {w 2.650 l 26.50 m 1 nx 1 wmin 2.650 lmin 26.50 \
+ rho 975 val 4875 dummy 0 dw 0.25 term 1.0 \
+ endcov 100 full_metal 1}
+}
+
+#----------------------------------------------------------------
+# rpp1: Specify all user-editable default values and those
+# needed by rp1_check
+#----------------------------------------------------------------
+
+proc sky130::mrp1_defaults {} {
+ return {w 0.330 l 1.650 m 1 nx 1 wmin 0.330 lmin 1.650 \
+ rho 48.2 val 241 dummy 0 dw 0.0 term 0.0 \
+ sterm 0.0 caplen 0.4 snake 0 \
+ glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \
+ full_metal 1}
+}
+
+# "term" is rho * 0.06, the distance between xpc edge and CONT.
+proc sky130::xhrpoly_defaults {} {
+ return {w 0.350 l 0.50 m 1 nx 1 wmin 0.350 lmin 0.50 \
+ rho 319.8 val 456.857 dummy 0 dw 0.0 term 19.188 \
+ sterm 0.0 caplen 0 glc 1 grc 1 gtc 1 gbc 1 \
+ full_metal 1}
+}
+
+# "term" is rho * 0.06, the distance between xpc edge and CONT.
+proc sky130::uhrpoly_defaults {} {
+ return {w 0.350 l 0.50 m 1 nx 1 wmin 0.350 lmin 0.50 \
+ rho 2000 val 2875.143 dummy 0 dw 0.0 term 120 \
+ sterm 0.0 caplen 0 \
+ glc 1 grc 1 gtc 1 gbc 1 full_metal 1}
+}
+
+#----------------------------------------------------------------
+# mrdn: Specify all user-editable default values and those
+# needed by rdn_check
+#----------------------------------------------------------------
+
+proc sky130::mrdn_defaults {} {
+ return {w 0.420 l 2.100 m 1 nx 1 wmin 0.42 lmin 2.10 \
+ rho 120 val 600.0 dummy 0 dw 0.05 term 0.0 \
+ sterm 0.0 caplen 0.4 snake 0 \
+ glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \
+ full_metal 1}
+}
+
+proc sky130::mrdn_hv_defaults {} {
+ return {w 0.420 l 2.100 m 1 nx 1 wmin 0.42 lmin 2.10 \
+ rho 120 val 600.0 dummy 0 dw 0.02 term 0.0 \
+ sterm 0.0 caplen 0.4 snake 0 \
+ glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \
+ full_metal 1}
+}
+
+#----------------------------------------------------------------
+# mrdp: Specify all user-editable default values and those
+# needed by rdp_check
+#----------------------------------------------------------------
+
+proc sky130::mrdp_defaults {} {
+ return {w 0.420 l 2.100 m 1 nx 1 wmin 0.42 lmin 2.10 \
+ rho 197 val 985.0 dummy 0 dw 0.02 term 0.0 \
+ sterm 0.0 caplen 0.60 snake 0 \
+ glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \
+ full_metal 1}
+}
+
+proc sky130::mrdp_hv_defaults {} {
+ return {w 0.420 l 2.100 m 1 nx 1 wmin 0.42 lmin 2.10 \
+ rho 197 val 985.0 dummy 0 dw 0.02 term 0.0 \
+ sterm 0.0 caplen 0.60 snake 0 \
+ glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \
+ full_metal 1}
+}
+
+#----------------------------------------------------------------
+# mrl1: Specify all user-editable default values and those needed
+# by mrl1_check
+#----------------------------------------------------------------
+
+proc sky130::mrl1_defaults {} {
+ return {w 0.170 l 0.170 m 1 nx 1 wmin 0.17 lmin 0.17 \
+ rho 12.8 val 12.8 dummy 0 dw 0.0 term 0.0 snake 0 \
+ roverlap 0}
+}
+
+#----------------------------------------------------------------
+# mrm1: Specify all user-editable default values and those needed
+# by mrm1_check
+#----------------------------------------------------------------
+
+proc sky130::mrm1_defaults {} {
+ return {w 0.140 l 0.140 m 1 nx 1 wmin 0.14 lmin 0.14 \
+ rho 0.125 val 0.125 dummy 0 dw 0.0 term 0.0 \
+ roverlap 0}
+}
+
+#----------------------------------------------------------------
+# mrm2: Specify all user-editable default values and those needed
+# by mrm2_check
+#----------------------------------------------------------------
+
+proc sky130::mrm2_defaults {} {
+ return {w 0.140 l 0.140 m 1 nx 1 wmin 0.14 lmin 0.14 \
+ rho 0.125 val 0.125 dummy 0 dw 0.0 term 0.0 \
+ roverlap 0}
+}
+
+#----------------------------------------------------------------
+# mrm3: Specify all user-editable default values and those needed
+# by mrm3_check
+#----------------------------------------------------------------
+
+proc sky130::mrm3_defaults {} {
+ return {w 0.300 l 0.300 m 1 nx 1 wmin 0.30 lmin 0.30 \
+ rho 0.047 val 0.047 dummy 0 dw 0.0 term 0.0 \
+ roverlap 0}
+}
+
+#----------------------------------------------------------------
+# Additional entries for mrm4 and mrm5, depending on the
+# back-end metal stack.
+#----------------------------------------------------------------
+
+#ifdef METAL5
+proc sky130::mrm4_defaults {} {
+ return {w 0.300 l 0.300 m 1 nx 1 wmin 0.30 lmin 0.30 \
+ rho 0.047 val 0.047 dummy 0 dw 0.0 term 0.0 \
+ roverlap 0}
+}
+proc sky130::mrm5_defaults {} {
+ return {w 1.600 l 1.600 m 1 nx 1 wmin 1.60 lmin 1.60 \
+ rho 0.029 val 0.029 dummy 0 dw 0.0 term 0.0 \
+ roverlap 0}
+}
+#endif (METAL5)
+
+#----------------------------------------------------------------
+# resistor: Conversion from SPICE netlist parameters to toolkit
+#----------------------------------------------------------------
+
+proc sky130::res_convert {parameters} {
+ set pdkparams [dict create]
+ dict for {key value} $parameters {
+ switch -nocase $key {
+ l -
+ w {
+ # Length and width are converted to units of microns
+ set value [magic::spice2float $value]
+ # set value [expr $value * 1e6]
+ set value [magic::3digitpastdecimal $value]
+ dict set pdkparams [string tolower $key] $value
+ }
+ }
+ }
+ return $pdkparams
+}
+
+#----------------------------------------------------------------
+
+proc sky130::xpwres_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrp1_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::xhrpoly_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::uhrpoly_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrdn_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrdp_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrdn_hv_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrdp_hv_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrl1_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrm1_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrm2_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+proc sky130::mrm3_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+
+#ifdef METAL5
+proc sky130::mrm4_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+proc sky130::mrm5_convert {parameters} {
+ return [sky130::res_convert $parameters]
+}
+#endif (METAL5)
+
+#----------------------------------------------------------------
+# resistor: Interactively specifies the fixed layout parameters
+#----------------------------------------------------------------
+
+proc sky130::res_dialog {device parameters} {
+ # Editable fields: w, l, t, nx, m, val
+ # Checked fields:
+
+ magic::add_entry val "Value (ohms)" $parameters
+ if {[dict exists $parameters snake]} {
+ sky130::compute_ltot $parameters
+ magic::add_message ltot "Total length (um)" $parameters
+ }
+ magic::add_entry l "Length (um)" $parameters
+ magic::add_entry w "Width (um)" $parameters
+ magic::add_entry nx "X Repeat" $parameters
+ magic::add_entry m "Y Repeat" $parameters
+ if {[dict exists $parameters endcov]} {
+ magic::add_entry endcov "End contact coverage (%)" $parameters
+ }
+
+ # magic::add_checkbox dummy "Add dummy" $parameters
+
+ if {[dict exists $parameters snake]} {
+ magic::add_checkbox snake "Use snake geometry" $parameters
+ }
+ if {[dict exists $parameters roverlap]} {
+ if {[dict exists $parameters endcov]} {
+ magic::add_checkbox roverlap "Overlap at end contact" $parameters
+ } else {
+ magic::add_checkbox roverlap "Overlap at ends" $parameters
+ }
+ }
+ magic::add_checkbox guard "Add guard ring" $parameters
+
+ if {[dict exists $parameters full_metal]} {
+ magic::add_checkbox full_metal "Full metal guard ring" $parameters
+ }
+ if {[dict exists $parameters glc]} {
+ magic::add_checkbox glc "Add left guard ring contact" $parameters
+ }
+ if {[dict exists $parameters grc]} {
+ magic::add_checkbox grc "Add right guard ring contact" $parameters
+ }
+ if {[dict exists $parameters gtc]} {
+ magic::add_checkbox gtc "Add top guard ring contact" $parameters
+ }
+ if {[dict exists $parameters gbc]} {
+ magic::add_checkbox gbc "Add bottom guard ring contact" $parameters
+ }
+
+ if {[dict exists $parameters snake]} {
+ magic::add_dependency sky130::res_recalc $device sky130 l w val nx snake
+ } else {
+ magic::add_dependency sky130::res_recalc $device sky130 l w val nx
+ }
+}
+
+#----------------------------------------------------------------
+
+proc sky130::xpwres_dialog {parameters} {
+ sky130::res_dialog xpwres $parameters
+}
+
+proc sky130::mrp1_dialog {parameters} {
+ sky130::res_dialog mrp1 $parameters
+}
+
+proc sky130::xhrpoly_dialog {parameters} {
+ sky130::res_dialog xhrpoly $parameters
+}
+
+proc sky130::uhrpoly_dialog {parameters} {
+ sky130::res_dialog uhrpoly $parameters
+}
+
+proc sky130::mrdn_dialog {parameters} {
+ sky130::res_dialog mrdn $parameters
+}
+
+proc sky130::mrdp_dialog {parameters} {
+ sky130::res_dialog mrdp $parameters
+}
+
+proc sky130::mrdn_hv_dialog {parameters} {
+ sky130::res_dialog mrdn_hv $parameters
+}
+
+proc sky130::mrdp_hv_dialog {parameters} {
+ sky130::res_dialog mrdp_hv $parameters
+}
+
+proc sky130::mrl1_dialog {parameters} {
+ sky130::res_dialog mrl1 $parameters
+}
+
+proc sky130::mrm1_dialog {parameters} {
+ sky130::res_dialog mrm1 $parameters
+}
+
+proc sky130::mrm2_dialog {parameters} {
+ sky130::res_dialog mrm2 $parameters
+}
+
+proc sky130::mrm3_dialog {parameters} {
+ sky130::res_dialog mrm3 $parameters
+}
+
+#ifdef METAL5
+proc sky130::mrm4_dialog {parameters} {
+ sky130::res_dialog mrm4 $parameters
+}
+proc sky130::mrm5_dialog {parameters} {
+ sky130::res_dialog mrm5 $parameters
+}
+#endif (METAL5)
+
+#----------------------------------------------------------------
+# Resistor: Draw a single device in straight geometry
+#----------------------------------------------------------------
+
+proc sky130::res_device {parameters} {
+ # Epsilon for avoiding round-off errors
+ set eps 0.0005
+
+ # Set local default values if they are not in parameters
+ set endcov 0 ;# percent coverage of end contacts
+ set roverlap 0 ;# overlap resistors at end contacts
+ set well_res_overlap 0 ;# not a well resistor
+ set end_contact_type "" ;# no contacts for metal resistors
+ set end_overlap_cont 0 ;# additional end overlap on sides
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ if {![dict exists $parameters end_contact_size]} {
+ set end_contact_size $contact_size
+ }
+
+ # Draw the resistor and endcaps
+ pushbox
+ box size 0 0
+ pushbox
+ set hw [/ $w 2.0]
+ set hl [/ $l 2.0]
+ box grow n ${hl}um
+ box grow s ${hl}um
+ box grow e ${hw}um
+ box grow w ${hw}um
+
+ pushbox
+ box grow n ${res_to_endcont}um
+ box grow s ${res_to_endcont}um
+ if {$well_res_overlap > 0} {
+ set well_extend [+ ${well_res_overlap} [/ ${end_contact_size} 2.0] ${end_surround}]
+ box grow n ${well_extend}um
+ box grow s ${well_extend}um
+ paint ${well_res_type}
+ } else {
+ paint ${end_type}
+ }
+ set cext [sky130::getbox]
+ popbox
+
+ if {$well_res_overlap > 0} {
+ erase ${well_res_type}
+ } else {
+ erase ${end_type}
+ }
+ paint ${res_type}
+ popbox
+
+ # Reduce contact sizes by (end type) surround so that
+ # the contact area edges match the device type width.
+ # (Minimum dimensions will be enforced by the contact drawing routine)
+ set epl [- ${w} [* ${end_surround} 2]] ;# end contact width
+
+ # Reduce end material size for well resistor types
+ if {$well_res_overlap > 0} {
+ set epl [- ${epl} [* ${well_res_overlap} 2]]
+ }
+
+ # Reduce by coverage percentage unless overlapping at contacts
+ if {(${roverlap} == 0) && (${endcov} > 0)} {
+ set cpl [* ${epl} [/ ${endcov} 100.0]]
+ } else {
+ set cpl $epl
+ }
+
+ # Ensure additional overlap of diffusion contact if required
+ set dov [* ${end_overlap_cont} 2]
+ if {[- ${epl} ${cpl}] < $dov} {
+ set cpl [- ${epl} $dov] ;# additional end contact width
+ }
+
+ set hepl [+ [/ ${epl} 2.0] ${end_surround}]
+ set hesz [/ ${end_contact_size} 2.0]
+
+ # LV substrate diffusion types have a different surround requirement
+ set lv_sub_types {"psd" "nsd"}
+ if {[lsearch $lv_sub_types $end_type] < 0} {
+ set hesz [+ ${hesz} ${end_surround}]
+ }
+
+ # Top end material & contact
+ pushbox
+ box move n ${hl}um
+ box move n ${res_to_endcont}um
+
+ pushbox
+ box size 0 0
+ box grow n ${hesz}um
+ box grow s ${hesz}um
+ box grow e ${hepl}um
+ box grow w ${hepl}um
+ paint ${end_type}
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ popbox
+
+ if {${end_contact_type} != ""} {
+ set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
+ ${end_surround} ${metal_surround} ${end_contact_size} \
+ ${end_type} ${end_contact_type} li horz]]
+ }
+ popbox
+
+ # Bottom end material & contact
+ pushbox
+ box move s ${hl}um
+ box move s ${res_to_endcont}um
+
+ pushbox
+ box size 0 0
+ box grow n ${hesz}um
+ box grow s ${hesz}um
+ box grow e ${hepl}um
+ box grow w ${hepl}um
+ paint ${end_type}
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ popbox
+
+ if {${end_contact_type} != ""} {
+ set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
+ ${end_surround} ${metal_surround} ${end_contact_size} \
+ ${end_type} ${end_contact_type} li horz]]
+ }
+ popbox
+
+ popbox
+ return $cext
+}
+
+#----------------------------------------------------------------
+# Resistor: Draw a single device in snake geometry
+#----------------------------------------------------------------
+
+proc sky130::res_snake_device {nf parameters} {
+ # nf is the number of fingers of the snake geometry
+
+ # Epsilon for avoiding round-off errors
+ set eps 0.0005
+
+ # Set local default values if they are not in parameters
+ set endcov 100 ;# percent coverage of end contacts
+ set well_res_overlap 0 ;# not a well resistor
+ set end_contact_type "" ;# no contacts for metal resistors
+ set mask_clearance 0 ;# additional length to clear mask
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ if {![dict exists $parameters end_contact_size]} {
+ set end_contact_size $contact_size
+ }
+
+ # Compute half width and length
+ set hw [/ $w 2.0]
+ set hl [/ $l 2.0]
+
+ # Reduce contact sizes by (end type) surround so that
+ # the contact area edges match the device type width.
+ # (Minimum dimensions will be enforced by the contact drawing routine)
+ set epl [- ${w} [* ${end_surround} 2]] ;# end contact width
+
+ # Reduce contact size for well resistor types
+ if {$well_res_overlap > 0} {
+ set epl [- ${epl} [* ${well_res_overlap} 2]]
+ }
+
+ # Reduce contact part of end by coverage percentage
+ if {${endcov} > 0} {
+ set cpl [* ${epl} [/ ${endcov} 100.0]]
+ } else {
+ set cpl $epl
+ }
+
+ set hepl [+ [/ ${epl} 2.0] ${end_surround}]
+ set hesz [+ [/ ${end_contact_size} 2.0] ${end_surround}]
+
+ pushbox
+ box size 0 0 ;# Position is taken from caller
+
+ # Front end contact (always bottom)
+ pushbox
+ box move s ${hl}um
+ pushbox
+ box move s ${mask_clearance}um
+ box move s ${res_to_endcont}um
+
+ pushbox
+ box size 0 0
+ box grow n ${hesz}um
+ box grow s ${hesz}um
+ box grow e ${hepl}um
+ box grow w ${hepl}um
+ paint ${end_type}
+ set cext [sky130::getbox]
+ popbox
+
+ if {${end_contact_type} != ""} {
+ set cext [sky130::draw_contact ${cpl} 0 \
+ ${end_surround} ${metal_surround} ${end_contact_size} \
+ ${end_type} ${end_contact_type} li horz]
+ }
+ popbox
+
+ # Draw portion between resistor end and contact.
+ box grow e ${hw}um
+ box grow w ${hw}um
+ pushbox
+ box grow s ${mask_clearance}um
+ paint ${res_type}
+ popbox
+ box move s ${mask_clearance}um
+ box grow s ${res_to_endcont}um
+ if {$well_res_overlap > 0} {
+ set well_extend [+ ${well_res_overlap} [/ ${end_contact_size} 2.0] ${end_surround}]
+ box grow s ${well_extend}um
+ paint ${well_res_type}
+ } else {
+ paint ${end_type}
+ }
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ popbox
+
+ # Draw the resistor and endcaps
+ pushbox
+ box grow n ${hl}um
+ box grow s ${hl}um
+ box grow e ${hw}um
+ box grow w ${hw}um
+
+ # Capture these extents in the bounding box in case both contacts
+ # are on one side.
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+
+ set deltax [+ ${res_spacing} ${w}]
+ set deltay [- ${l} ${w}]
+ for {set i 0} {$i < [- $nf 1]} {incr i} {
+ paint ${res_type}
+ pushbox
+ if {[% $i 2] == 0} {
+ box move n ${deltay}um
+ }
+ box height ${w}um
+ box width ${deltax}um
+ paint ${res_type}
+ popbox
+ box move e ${deltax}um
+ }
+ paint ${res_type}
+ # Capture these extents in the bounding box
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ popbox
+
+ # Move box to last finger
+ set lastf [* [- $nf 1] $deltax]
+ box move e ${lastf}um
+
+ # Back-end contact (top or bottom, depending if odd or even turns)
+ pushbox
+
+ if {[% $nf 2] == 1} {
+ set dir n
+ } else {
+ set dir s
+ }
+ box move $dir ${hl}um
+ pushbox
+ box move $dir ${mask_clearance}um
+ box move $dir ${res_to_endcont}um
+
+ pushbox
+ box size 0 0
+ box grow n ${hesz}um
+ box grow s ${hesz}um
+ box grow e ${hepl}um
+ box grow w ${hepl}um
+ paint ${end_type}
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ popbox
+
+ if {${end_contact_type} != ""} {
+ set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
+ ${end_surround} ${metal_surround} ${end_contact_size} \
+ ${end_type} ${end_contact_type} li horz]]
+ }
+ popbox
+ # Draw portion between resistor end and contact.
+ box grow e ${hw}um
+ box grow w ${hw}um
+ pushbox
+ box grow $dir ${mask_clearance}um
+ paint ${res_type}
+ popbox
+ box move $dir ${mask_clearance}um
+ box grow $dir ${res_to_endcont}um
+
+ if {$well_res_overlap > 0} {
+ set well_extend [+ ${well_res_overlap} [/ ${end_contact_size} 2.0] ${end_surround}]
+ box grow $dir ${well_extend}um
+ paint ${well_res_type}
+ } else {
+ paint ${end_type}
+ }
+ popbox
+
+ popbox
+ return $cext
+}
+
+#----------------------------------------------------------------
+# Resistor: Draw the tiled device
+#----------------------------------------------------------------
+
+proc sky130::res_draw {parameters} {
+ tech unlock *
+ set savesnap [snap]
+ snap internal
+
+ # Set defaults if they are not in parameters
+ set snake 0 ;# some resistors don't allow snake geometry
+ set roverlap 0 ;# overlap resistors at contacts
+ set guard 1 ;# draw a guard ring
+ set plus_diff_type nsd ;# guard ring diffusion type
+ set overlap_compress 0 ;# special Y distance compression
+ set well_res_overlap 0 ;# additional well extension behind contact
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # For devices where inter-device space is smaller than device-to-guard ring
+ if {![dict exists $parameters end_to_end_space]} {
+ set end_to_end_space $end_spacing
+ }
+
+ if {![dict exists $parameters end_contact_size]} {
+ set end_contact_size $contact_size
+ }
+
+ # Normalize distance units to microns
+ set w [magic::spice2float $w]
+ set l [magic::spice2float $l]
+
+ pushbox
+ box values 0 0 0 0
+
+ # Determine the base device dimensions by drawing one device
+ # while all layers are locked (nothing drawn). This allows the
+ # base drawing routine to do complicated geometry without having
+ # to duplicate it here with calculations.
+
+ tech lock *
+ set nf $nx
+ if {($snake == 1) && ($nx == 1)} {set snake 0}
+ if {$snake == 1} {
+ set bbox [sky130::res_snake_device $nf $parameters]
+ set nx 1
+ } else {
+ set bbox [sky130::res_device $parameters]
+ }
+ # puts stdout "Diagnostic: Device bounding box e $bbox (um)"
+ tech unlock *
+
+ set fw [- [lindex $bbox 2] [lindex $bbox 0]]
+ set fh [- [lindex $bbox 3] [lindex $bbox 1]]
+ set lw [+ [lindex $bbox 2] [lindex $bbox 0]]
+ set lh [+ [lindex $bbox 3] [lindex $bbox 1]]
+
+ # Determine tile width and height (depends on overlap)
+ # Snake resistors cannot overlap.
+ # However, snake resistors with an odd number of fingers can
+ # compress the space if overlap_compress is defined
+
+ if {($roverlap == 1) && ($snake == 1) && ([% $nf 2] == 1) && ($m > 1)} {
+ set dy [- $fh $overlap_compress]
+ } elseif {($roverlap == 0) || ($snake == 1)} {
+ set dy [+ $fh $end_to_end_space]
+ } else {
+ # overlap poly
+ set dy [- $fh [+ [* [+ $end_surround $well_res_overlap] 2.0] $end_contact_size]]
+ }
+ set dx [+ $fw $res_spacing]
+
+ # Determine core width and height
+ set corex [+ [* [- $nx 1] $dx] $fw]
+ set corey [+ [* [- $m 1] $dy] $fh]
+ set corellx [/ [+ [- $corex $fw] $lw] 2.0]
+ set corelly [/ [+ [- $corey $fh] $lh] 2.0]
+
+ set lv_sub_types {"psd" "nsd"}
+ if {[lsearch $lv_sub_types $plus_diff_type] >= 0} {
+ set guard_diff_surround 0
+ } else {
+ set guard_diff_surround ${diff_surround}
+ }
+
+ if {$guard != 0} {
+ # Calculate guard ring size (measured to contact center)
+ set gx [+ $corex [* 2.0 [+ $res_diff_spacing $guard_diff_surround]] $contact_size]
+ set gy [+ $corey [* 2.0 [+ $end_spacing $guard_diff_surround]] $contact_size]
+
+ # Draw the guard ring first, because well resistors are on the substrate plane
+ sky130::guard_ring $gx $gy $parameters
+ }
+
+ pushbox
+ box move w ${corellx}um
+ box move s ${corelly}um
+ # puts "Device position at = [sky130::getbox]"
+ for {set xp 0} {$xp < $nx} {incr xp} {
+ pushbox
+ for {set yp 0} {$yp < $m} {incr yp} {
+ if {$snake == 1} {
+ sky130::res_snake_device $nf $parameters
+ } else {
+ sky130::res_device $parameters
+ }
+ box move n ${dy}um
+ }
+ popbox
+ box move e ${dx}um
+ }
+ popbox
+ popbox
+
+ snap $savesnap
+ tech revert
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrp1_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type npres \
+ end_type poly \
+ end_contact_type pc \
+ plus_diff_type nsd \
+ plus_contact_type nsc \
+ sub_type nwell \
+ end_surround $poly_surround \
+ end_spacing 0.48 \
+ end_to_end_space 0.52 \
+ res_to_endcont $res_to_cont \
+ res_spacing $poly_spacing \
+ res_diff_spacing 0.48 \
+ mask_clearance 0.52 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::xhrpoly_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type ppres \
+ end_type xpc \
+ end_contact_type xpc \
+ end_contact_size 0 \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ sub_type psub \
+ end_surround $poly_surround \
+ end_spacing 0.48 \
+ end_to_end_space 0.52 \
+ end_contact_size 0.19 \
+ res_to_endcont 1.985 \
+ res_spacing 1.24 \
+ res_diff_spacing 0.48 \
+ mask_clearance 0.52 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::uhrpoly_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type xpres \
+ end_type xpc \
+ end_contact_type xpc \
+ end_contact_size 0 \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ sub_type psub \
+ end_surround $poly_surround \
+ end_spacing 0.48 \
+ end_to_end_space 0.52 \
+ end_contact_size 0.19 \
+ res_to_endcont 1.985 \
+ res_spacing 1.24 \
+ res_diff_spacing 0.48 \
+ mask_clearance 0.52 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrdn_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type rdn \
+ end_type ndiff \
+ end_contact_type ndc \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ sub_type psub \
+ end_surround $diff_surround \
+ end_spacing 0.44 \
+ res_to_endcont 0.37 \
+ res_spacing 0.30 \
+ res_diff_spacing 0.44 \
+ mask_clearance 0.22 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrdn_hv_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type mvrdn \
+ end_type mvndiff \
+ end_contact_type mvndc \
+ plus_diff_type mvpsd \
+ plus_contact_type mvpsc \
+ sub_type psub \
+ end_surround $diff_surround \
+ end_spacing 0.44 \
+ res_to_endcont 0.37 \
+ res_spacing 0.30 \
+ res_diff_spacing 0.44 \
+ mask_clearance 0.22 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrdp_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type rdp \
+ end_type pdiff \
+ end_contact_type pdc \
+ plus_diff_type nsd \
+ plus_contact_type nsc \
+ sub_type nwell \
+ end_surround $diff_surround \
+ end_spacing 0.44 \
+ res_to_endcont 0.37 \
+ res_spacing $diff_spacing \
+ res_diff_spacing 0.44 \
+ mask_clearance 0.22 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrdp_hv_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ res_type mvrdp \
+ end_type mvpdiff \
+ end_contact_type mvpdc \
+ plus_diff_type mvnsd \
+ plus_contact_type mvnsc \
+ sub_type nwell \
+ end_surround $diff_surround \
+ guard_sub_surround 0.33 \
+ end_spacing 0.44 \
+ res_to_endcont 0.37 \
+ res_spacing 0.30 \
+ res_diff_spacing 0.44 \
+ mask_clearance 0.22 \
+ overlap_compress 0.36 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::xpwres_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ well_res_type pwell \
+ res_type rpw \
+ end_type psd \
+ end_contact_type psc \
+ plus_diff_type nsd \
+ plus_contact_type nsc \
+ sub_type dnwell \
+ sub_surround 0.23 \
+ guard_sub_type nwell \
+ guard_sub_surround 0.63 \
+ end_surround $diff_surround \
+ end_spacing 0.63 \
+ end_to_end_space 1.15 \
+ end_overlap_cont 0.06 \
+ end_contact_size 0.53 \
+ overlap_compress -0.17 \
+ res_to_endcont 0.265 \
+ res_spacing 1.4 \
+ res_diff_spacing 0.63 \
+ well_res_overlap 0.2 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrl1_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 0 \
+ res_type rli \
+ end_type li \
+ end_surround 0.0 \
+ end_spacing 0.0 \
+ res_to_endcont 0.2 \
+ end_to_end_space 0.23 \
+ res_spacing $metal_spacing \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrm1_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 0 \
+ res_type rm1 \
+ end_type m1 \
+ end_surround 0.0 \
+ end_spacing 0.0 \
+ end_to_end_space 0.28 \
+ res_to_endcont 0.2 \
+ res_spacing $mmetal_spacing \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrm2_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 0 \
+ res_type rm2 \
+ end_type m2 \
+ end_surround 0.0 \
+ end_spacing 0.0 \
+ end_to_end_space 0.28 \
+ res_to_endcont 0.2 \
+ res_spacing $mmetal_spacing \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+proc sky130::mrm3_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 0 \
+ res_type rm3 \
+ end_type m3 \
+ end_surround 0.0 \
+ end_spacing 0.0 \
+ end_to_end_space 0.28 \
+ res_to_endcont 0.2 \
+ res_spacing $mmetal_spacing \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+
+#ifdef METAL5
+proc sky130::mrm4_draw {parameters} {
+
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 0 \
+ res_type rm4 \
+ end_type m4 \
+ end_surround 0.0 \
+ end_spacing 0.0 \
+ end_to_end_space 0.28 \
+ res_to_endcont 0.2 \
+ res_spacing $mmetal_spacing \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+
+proc sky130::mrm5_draw {parameters} {
+ # Set a local variable for each rule in ruleset
+ foreach key [dict keys $sky130::ruleset] {
+ set $key [dict get $sky130::ruleset $key]
+ }
+
+ set newdict [dict create \
+ guard 0 \
+ res_type rm5 \
+ end_type m5 \
+ end_surround 0.0 \
+ end_spacing 0.0 \
+ end_to_end_space 1.6 \
+ res_to_endcont 0.2 \
+ res_spacing $mmetal_spacing \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::res_draw $drawdict]
+}
+#endif (METAL5)
+
+#----------------------------------------------------------------
+# Resistor total length computation
+#----------------------------------------------------------------
+
+proc sky130::compute_ltot {parameters} {
+ # In case snake not defined
+ set snake 0
+ set caplen 0
+
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ set l [magic::spice2float $l]
+ set l [magic::3digitpastdecimal $l]
+
+ # Compute total length. Use catch to prevent error in batch/scripted mode.
+ if {$snake == 1} {
+ catch {set magic::ltot_val [expr ($caplen * ($nx - 1)) + ($l * $nx) + ($nx - 1)]}
+ } else {
+ catch {set magic::ltot_val $l}
+ }
+}
+
+#----------------------------------------------------------------
+# resistor: Check device parameters for out-of-bounds values
+#----------------------------------------------------------------
+
+proc sky130::res_check {device parameters} {
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ set snake 0
+ set sterm 0.0
+ set caplen 0
+
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set w [magic::spice2float $w]
+ set w [magic::3digitpastdecimal $w]
+ set l [magic::spice2float $l]
+ set l [magic::3digitpastdecimal $l]
+
+ set val [magic::spice2float $val]
+ set rho [magic::spice2float $rho]
+
+ # nf, m must be integer
+ if {![string is int $nx]} {
+ puts stderr "X repeat must be an integer!"
+ dict set parameters nx 1
+ }
+ if {![string is int $m]} {
+ puts stderr "Y repeat must be an integer!"
+ dict set parameters m 1
+ }
+
+ # Width always needs to be specified
+ if {$w < $wmin} {
+ puts stderr "Resistor width must be >= $wmin um"
+ dict set parameters w $wmin
+ }
+ # Val and W specified - no L
+ if {$l == 0} {
+ set l [expr ($w - $dw) * $val / $rho]
+ set l [magic::3digitpastdecimal $l]
+ set stringval [magic::float2spice $val]
+ dict set parameters l [magic::float2spice [expr $l * 1e-6]]
+ # L and W specified - ignore Val if specified
+ } else {
+ if {$snake == 0} {
+ set val [expr (2 * $term + $l * $rho) / ($w - $dw)]
+ } else {
+ set val [expr $rho * ($nx - 1) + ((2 * ($term + $sterm)) \
+ + ($rho * $l * $nx) + ($rho * $caplen * ($nx - 1))) \
+ / ($w - $dw)]
+ }
+ set val [magic::float2spice $val]
+ dict set parameters val $val
+ }
+ if {$l < $lmin} {
+ puts stderr "Resistor length must be >= $lmin um"
+ dict set parameters l $lmin
+ }
+ if {$nx < 1} {
+ puts stderr "X repeat must be >= 1"
+ dict set parameters nx 1
+ }
+ if {$m < 1} {
+ puts stderr "Y repeat must be >= 1"
+ dict set parameters m 1
+ }
+
+ # Snake resistors cannot have width greater than length
+ if {$snake == 1} {
+ if {$w > $l} {
+ puts stderr "Snake resistor width must be < length"
+ dict set parameters w $l
+ }
+ }
+
+ # Diffusion resistors must satisfy diffusion-to-tap spacing of 20um.
+ # Therefore the maximum of guard ring width or height cannot exceed 40um.
+ # If in violation, reduce counts first, as these are easiest to recover
+ # by duplicating the device and overlapping the wells.
+ if {$device == "rdn" || $device == "rdp"} {
+ set origm $m
+ set orignx $nx
+ while true {
+ set xext [expr ($w + 0.8) * $nx + 1.0]
+ set yext [expr ($l + 1.7) * $m + 1.7]
+ if {[expr min($xext, $yext)] > 40.0} {
+ if {$yext > 40.0 && $m > 1} {
+ incr m -1
+ } elseif {$xext > 40.0 && $nx > 1} {
+ incr nx -1
+ } elseif {$yext > 40.0} {
+ set l 36.6
+ puts -nonewline stderr "Diffusion resistor length must be < 36.6 um"
+ puts stderr " to avoid tap spacing violation."
+ dict set parameters l $l
+ } elseif {$xext > 40.0} {
+ set w 38.2
+ puts -nonewline stderr "Diffusion resistor width must be < 38.2 um"
+ puts stderr " to avoid tap spacing violation."
+ dict set parameters w $w
+ }
+ } else {
+ break
+ }
+ }
+ if {$m != $origm} {
+ puts stderr "Y repeat reduced to prevent tap distance violation"
+ dict set parameters m $m
+ }
+ if {$nx != $orignx} {
+ puts stderr "X repeat reduced to prevent tap distance violation"
+ dict set parameters nx $nx
+ }
+ }
+ sky130::compute_ltot $parameters
+ return $parameters
+}
+
+#----------------------------------------------------------------
+
+proc sky130::xpwres_check {parameters} {
+ return [sky130::res_check xpwres $parameters]
+}
+
+proc sky130::mrp1_check {parameters} {
+ return [sky130::res_check mrp1 $parameters]
+}
+
+proc sky130::xhrpoly_check {parameters} {
+ return [sky130::res_check xhrpoly $parameters]
+}
+
+proc sky130::uhrpoly_check {parameters} {
+ return [sky130::res_check uhrpoly $parameters]
+}
+
+proc sky130::mrdn_check {parameters} {
+ return [sky130::res_check mrdn $parameters]
+}
+
+proc sky130::mrdp_check {parameters} {
+ return [sky130::res_check mrdp $parameters]
+}
+
+proc sky130::mrdn_hv_check {parameters} {
+ return [sky130::res_check mrdn_hv $parameters]
+}
+
+proc sky130::mrdp_hv_check {parameters} {
+ return [sky130::res_check mrdp_hv $parameters]
+}
+
+proc sky130::mrl1_check {parameters} {
+ return [sky130::res_check mrl1 $parameters]
+}
+
+proc sky130::mrm1_check {parameters} {
+ return [sky130::res_check mrm1 $parameters]
+}
+
+proc sky130::mrm2_check {parameters} {
+ return [sky130::res_check mrm2 $parameters]
+}
+
+proc sky130::mrm3_check {parameters} {
+ return [sky130::res_check mrm3 $parameters]
+}
+
+#ifdef METAL5
+proc sky130::mrm4_check {parameters} {
+ return [sky130::res_check mrm4 $parameters]
+}
+proc sky130::mrm5_check {parameters} {
+ return [sky130::res_check mrm5 $parameters]
+}
+#endif (METAL5)
+
+#----------------------------------------------------------------
+# MOS defaults:
+#----------------------------------------------------------------
+# w = Gate width
+# l = Gate length
+# m = Multiplier
+# nf = Number of fingers
+# diffcov = Diffusion contact coverage
+# polycov = Poly contact coverage
+# topc = Top gate contact
+# botc = Bottom gate contact
+# guard = Guard ring
+#
+# (not user-editable)
+#
+# lmin = Gate minimum length
+# wmin = Gate minimum width
+#----------------------------------------------------------------
+
+#----------------------------------------------------------------
+# pmos: Specify all user-editable default values and those
+# needed by mos_check
+#----------------------------------------------------------------
+
+proc sky130::pshort_defaults {} {
+ return {w 0.42 l 0.15 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 1 lmin 0.15 wmin 0.42 \
+ compatible {pshort plowvt phighvt phv} full_metal 1}
+}
+
+proc sky130::plowvt_defaults {} {
+ return {w 0.42 l 0.35 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 1 lmin 0.35 wmin 0.42 \
+ compatible {pshort plowvt phighvt phv} full_metal 1}
+}
+
+proc sky130::phighvt_defaults {} {
+ return {w 0.42 l 0.15 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 1 lmin 0.15 wmin 0.42 \
+ compatible {pshort plowvt phighvt phv} full_metal 1}
+}
+
+proc sky130::phv_defaults {} {
+ return {w 0.42 l 0.50 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 1 lmin 0.50 wmin 0.42 \
+ compatible {pshort plowvt phighvt phv} full_metal 1}
+}
+
+#----------------------------------------------------------------
+# nmos: Specify all user-editable default values and those
+# needed by mos_check
+#----------------------------------------------------------------
+
+proc sky130::nshort_defaults {} {
+ return {w 0.420 l 0.150 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 1 lmin 0.15 wmin 0.42 \
+ compatible {nshort nlowvt sonos_e nhv nhvnative} full_metal 1}
+}
+
+proc sky130::nlowvt_defaults {} {
+ return {w 0.420 l 0.150 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 1 lmin 0.15 wmin 0.42 \
+ compatible {nshort nlowvt sonos_e nhv nhvnative} full_metal 1}
+}
+
+proc sky130::sonos_e_defaults {} {
+ return {w 0.420 l 0.150 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 1 lmin 0.15 wmin 0.42 \
+ compatible {nshort nlowvt sonos_e nhv nhvnative} full_metal 1}
+}
+
+proc sky130::nhv_defaults {} {
+ return {w 0.42 l 0.50 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 1 lmin 0.50 wmin 0.42 \
+ compatible {nshort nlowvt sonos_e nhv nhvnative} full_metal 1}
+}
+
+proc sky130::nhvnative_defaults {} {
+ return {w 0.42 l 0.50 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 1 lmin 0.50 wmin 0.42 \
+ compatible {nshort nlowvt sonos_e nhv nhvnative} full_metal 1}
+}
+
+#----------------------------------------------------------------
+# mos varactor: Specify all user-editable default values and those
+# needed by mosvc_check
+#----------------------------------------------------------------
+
+proc sky130::xcnwvc_defaults {} {
+ return {w 1.0 l 0.18 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 1 lmin 0.18 wmin 1.0 \
+ compatible {xcnwvc xcnwvc2 xchvnwc} full_metal 1}
+}
+
+proc sky130::xcnwvc2_defaults {} {
+ return {w 1.0 l 0.18 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 1 lmin 0.18 wmin 1.0 \
+ compatible {xcnwvc xcnwvc2 xchvnwc} full_metal 1}
+}
+
+proc sky130::xchvnwc_defaults {} {
+ return {w 1.0 l 0.50 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 1 lmin 0.50 wmin 1.0 \
+ compatible {xcnwvc xcnwvc2 xchvnwc} full_metal 1}
+}
+
+#----------------------------------------------------------------
+# mos: Conversion from SPICE netlist parameters to toolkit
+#----------------------------------------------------------------
+
+proc sky130::mos_convert {parameters} {
+ set pdkparams [dict create]
+ dict for {key value} $parameters {
+ switch -nocase $key {
+ l -
+ w {
+ # Length and width are converted to units of microns
+ set value [magic::spice2float $value]
+ # set value [expr $value * 1e6]
+ set value [magic::3digitpastdecimal $value]
+ dict set pdkparams [string tolower $key] $value
+ }
+ m {
+ # M value in an expression like '1*1' convert to
+ # M and NF
+ if {[regexp {\'([0-9]+)\*([0-9]+)\'} $value valid m nf]} {
+ dict set pdkparams [string tolower $key] $m
+ dict set pdkparams nf $nf
+ } else {
+ dict set pdkparams [string tolower $key] $value
+ }
+ }
+ }
+ }
+ return $pdkparams
+}
+
+#----------------------------------------------------------------
+
+proc sky130::nshort_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::nlowvt_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::sonos_e_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::nhv_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::nhvnative_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::pshort_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::plowvt_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::phighvt_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::phv_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::xcnwvc_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::xcnwvc2_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+proc sky130::xchvnwc_convert {parameters} {
+ return [sky130::mos_convert $parameters]
+}
+
+#----------------------------------------------------------------
+# mos: Interactively specifies the fixed layout parameters
+#----------------------------------------------------------------
+
+proc sky130::mos_dialog {device parameters} {
+ # Editable fields: w, l, nf, m, diffcov, polycov
+ # Checked fields: topc, botc
+ # For specific devices, gate type is a selection list
+
+ magic::add_entry w "Width (um)" $parameters
+ magic::add_entry l "Length (um)" $parameters
+ magic::add_entry nf "Fingers" $parameters
+ magic::add_entry m "M" $parameters
+
+ if {[dict exists $parameters compatible]} {
+ set sellist [dict get $parameters compatible]
+ magic::add_selectlist gencell "Device type" $sellist $parameters $device
+ }
+
+ magic::add_entry diffcov "Diffusion contact coverage (%)" $parameters
+ magic::add_entry polycov "Poly contact coverage (%)" $parameters
+ magic::add_entry rlcov "Guard ring contact coverage (%)" $parameters
+ if {[dict exists $parameters gbc]} {
+ magic::add_entry tbcov "Guard ring top/bottom contact coverage (%)" $parameters
+ }
+
+ magic::add_checkbox poverlap "Overlap at poly contact" $parameters
+ 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
+
+ magic::add_checkbox guard "Add guard ring" $parameters
+ magic::add_checkbox full_metal "Full metal guard ring" $parameters
+ magic::add_checkbox glc "Add left guard ring contact" $parameters
+ magic::add_checkbox grc "Add right guard ring contact" $parameters
+ if {[dict exists $parameters gbc]} {
+ magic::add_checkbox gbc "Add bottom guard ring contact" $parameters
+ }
+ if {[dict exists $parameters gtc]} {
+ magic::add_checkbox gtc "Add top guard ring contact" $parameters
+ }
+}
+
+#----------------------------------------------------------------
+
+proc sky130::nshort_dialog {parameters} {
+ sky130::mos_dialog nshort $parameters
+}
+
+proc sky130::nlowvt_dialog {parameters} {
+ sky130::mos_dialog nlowvt $parameters
+}
+
+proc sky130::sonos_e_dialog {parameters} {
+ sky130::mos_dialog sonos_e $parameters
+}
+
+proc sky130::nhv_dialog {parameters} {
+ sky130::mos_dialog nhv $parameters
+}
+
+proc sky130::nhvnative_dialog {parameters} {
+ sky130::mos_dialog nhvnative $parameters
+}
+
+proc sky130::pshort_dialog {parameters} {
+ sky130::mos_dialog pshort $parameters
+}
+
+proc sky130::plowvt_dialog {parameters} {
+ sky130::mos_dialog plowvt $parameters
+}
+
+proc sky130::phighvt_dialog {parameters} {
+ sky130::mos_dialog phighvt $parameters
+}
+
+proc sky130::phv_dialog {parameters} {
+ sky130::mos_dialog phv $parameters
+}
+
+proc sky130::xcnwvc_dialog {parameters} {
+ sky130::mos_dialog xcnwvc $parameters
+}
+
+proc sky130::xcnwvc2_dialog {parameters} {
+ sky130::mos_dialog xcnwvc2 $parameters
+}
+
+proc sky130::xchvnwc_dialog {parameters} {
+ sky130::mos_dialog xchvnwc $parameters
+}
+
+#----------------------------------------------------------------
+# getbox: Get the current cursor box, in microns
+#----------------------------------------------------------------
+
+proc sky130::getbox {} {
+ set curbox [box values]
+ set newbox []
+ set oscale [cif scale out]
+ for {set i 0} {$i < 4} {incr i} {
+ set v [* [lindex $curbox $i] $oscale]
+ lappend newbox $v
+ }
+ return $newbox
+}
+
+#----------------------------------------------------------------
+# unionbox: Get the union bounding box of box1 and box2
+#----------------------------------------------------------------
+
+proc sky130::unionbox {box1 box2} {
+ set newbox []
+ for {set i 0} {$i < 2} {incr i} {
+ set v [lindex $box1 $i]
+ set o [lindex $box2 $i]
+ if {$v < $o} {
+ lappend newbox $v
+ } else {
+ lappend newbox $o
+ }
+ }
+ for {set i 2} {$i < 4} {incr i} {
+ set v [lindex $box1 $i]
+ set o [lindex $box2 $i]
+ if {$v > $o} {
+ lappend newbox $v
+ } else {
+ lappend newbox $o
+ }
+ }
+ return $newbox
+}
+
+#----------------------------------------------------------------
+# Draw a contact
+#----------------------------------------------------------------
+
+proc sky130::draw_contact {w h s o x atype ctype mtype {orient vert}} {
+
+ # Draw a minimum-size diff contact centered at current position
+ # w is width, h is height. Minimum size ensured.
+ # x is contact size
+ # s is contact diffusion (or poly) surround
+ # o is contact metal surround
+ # atype is active (e.g., ndiff) or bottom metal if a via
+ # ctype is contact (e.g., ndc)
+ # mtype is metal (e.g., m1) or top metal if a via
+ # orient is the orientation of the contact
+
+ # Set orientations for the bottom material based on material type.
+ # Substrate diffusions (tap) need not overlap the contact in all
+ # directions, but other (diff) types do. The metal (local
+ # interconnect) layer always overlaps in two directions only.
+
+ set lv_sub_types {"psd" "nsd"}
+ if {[lsearch $lv_sub_types $atype] >= 0} {
+ set aorient $orient
+ } else {
+ set aorient "full"
+ }
+
+ pushbox
+ box size 0 0
+ if {$w < $x} {set w $x}
+ if {$h < $x} {set h $x}
+ set hw [/ $w 2.0]
+ set hh [/ $h 2.0]
+ box grow n ${hh}um
+ box grow s ${hh}um
+ box grow e ${hw}um
+ box grow w ${hw}um
+ paint ${ctype}
+ pushbox
+ # Bottom layer surrounded on sides as declared by aorient
+ if {($aorient == "vert") || ($aorient == "full")} {
+ box grow n ${s}um
+ box grow s ${s}um
+ }
+ if {($aorient == "horz") || ($aorient == "full")} {
+ box grow e ${s}um
+ box grow w ${s}um
+ }
+ paint ${atype}
+ set extents [sky130::getbox]
+ popbox
+ # Top layer surrounded on sides as declared by orient
+ if {($orient == "vert") || ($orient == "full")} {
+ box grow n ${o}um
+ box grow s ${o}um
+ }
+ if {($orient == "horz") || ($orient == "full")} {
+ box grow e ${o}um
+ box grow w ${o}um
+ }
+ paint ${mtype}
+ popbox
+ return $extents
+}
+
+#----------------------------------------------------------------
+# Draw a guard ring
+#----------------------------------------------------------------
+
+proc sky130::guard_ring {gw gh parameters} {
+
+ # Set local default values if they are not in parameters
+ set rlcov 100 ;# Right-left contact coverage percentage
+ set tbcov 100 ;# Top-bottom contact coverage percentage
+ set grc 1 ;# Draw right side contact
+ set glc 1 ;# Draw left side contact
+ set gtc 1 ;# Draw right side contact
+ set gbc 1 ;# Draw left side contact
+ set full_metal 1 ;# Draw full (continuous) metal ring
+ set guard_sub_type pwell ;# substrate type under guard ring
+ set guard_sub_surround 0 ;# substrate type surrounds guard ring
+ set plus_diff_type nsd ;# guard ring diffusion type
+ set plus_contact_type nsc ;# guard ring diffusion contact type
+ set sub_type pwell ;# substrate type
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Set guard_sub_type to sub_type if it is not defined
+ if {![dict exists $parameters guard_sub_type]} {
+ set guard_sub_type $sub_type
+ }
+
+ set hx [/ $contact_size 2.0]
+ set hw [/ $gw 2.0]
+ set hh [/ $gh 2.0]
+
+ # Watch for (LV) substrate diffusion types, which have a different
+ # contact surround amount depending on the direction
+
+ set lv_sub_types {"psd" "nsd"}
+ if {[lsearch $lv_sub_types $plus_diff_type] >= 0} {
+ set diff_surround 0
+ }
+
+ # Compute diffusion width
+ set difft [+ $contact_size $diff_surround $diff_surround]
+ set hdifft [/ $difft 2.0]
+ # Compute guard ring diffusion width and height
+ set hdiffw [/ [+ $gw $difft] 2.0]
+ set hdiffh [/ [+ $gh $difft] 2.0]
+
+ pushbox
+ box size 0 0
+
+ pushbox
+ box move n ${hh}um
+ box grow n ${hdifft}um
+ box grow s ${hdifft}um
+ box grow e ${hdiffw}um
+ box grow w ${hdiffw}um
+ paint $plus_diff_type
+ if {$guard_sub_surround > 0} {
+ box grow c ${guard_sub_surround}um
+ paint $guard_sub_type
+ }
+ popbox
+ pushbox
+ box move s ${hh}um
+ box grow n ${hdifft}um
+ box grow s ${hdifft}um
+ box grow e ${hdiffw}um
+ box grow w ${hdiffw}um
+ paint $plus_diff_type
+ if {$guard_sub_surround > 0} {
+ box grow c ${guard_sub_surround}um
+ paint $guard_sub_type
+ }
+ popbox
+ pushbox
+ box move e ${hw}um
+ box grow e ${hdifft}um
+ box grow w ${hdifft}um
+ box grow n ${hdiffh}um
+ box grow s ${hdiffh}um
+ paint $plus_diff_type
+ if {$guard_sub_surround > 0} {
+ box grow c ${guard_sub_surround}um
+ paint $guard_sub_type
+ }
+ popbox
+ pushbox
+ box move w ${hw}um
+ box grow e ${hdifft}um
+ box grow w ${hdifft}um
+ box grow n ${hdiffh}um
+ box grow s ${hdiffh}um
+ paint $plus_diff_type
+ if {$guard_sub_surround > 0} {
+ box grow c ${guard_sub_surround}um
+ paint $guard_sub_type
+ }
+ popbox
+
+ if {$full_metal} {
+ set hmetw [/ [+ $gw $contact_size] 2.0]
+ set hmeth [/ [+ $gh $contact_size] 2.0]
+ pushbox
+ box move n ${hh}um
+ box grow n ${hx}um
+ box grow s ${hx}um
+ box grow e ${hmetw}um
+ box grow w ${hmetw}um
+ paint li
+ popbox
+ pushbox
+ box move s ${hh}um
+ box grow n ${hx}um
+ box grow s ${hx}um
+ box grow e ${hmetw}um
+ box grow w ${hmetw}um
+ paint li
+ popbox
+ pushbox
+ box move e ${hw}um
+ box grow e ${hx}um
+ box grow w ${hx}um
+ box grow n ${hmeth}um
+ box grow s ${hmeth}um
+ paint li
+ popbox
+ pushbox
+ box move w ${hw}um
+ box grow e ${hx}um
+ box grow w ${hx}um
+ box grow n ${hmeth}um
+ box grow s ${hmeth}um
+ paint li
+ popbox
+ }
+
+ # Set guard ring height so that contact metal reaches to end, scale by $per
+ # set ch [* [+ $gh $contact_size [* $metal_surround -2.0]] [/ $rlcov 100.0]]
+ set ch [* [- $gh $contact_size [* [+ $metal_surround $metal_spacing] \
+ 2.0]] [/ $rlcov 100.0]]
+ if {$ch < $contact_size} {set ch $contact_size}
+
+ # Set guard ring width so that contact metal reaches to side contacts
+ set cw [* [- $gw $contact_size [* [+ $metal_surround $metal_spacing] \
+ 2.0]] [/ $tbcov 100.0]]
+ if {$cw < $contact_size} {set cw $contact_size}
+
+ if {$tbcov > 0.0} {
+ if {$gtc == 1} {
+ pushbox
+ box move n ${hh}um
+ sky130::draw_contact $cw 0 $diff_surround $metal_surround \
+ $contact_size $plus_diff_type $plus_contact_type li horz
+ popbox
+ }
+ if {$gbc == 1} {
+ pushbox
+ box move s ${hh}um
+ sky130::draw_contact $cw 0 $diff_surround $metal_surround \
+ $contact_size $plus_diff_type $plus_contact_type li horz
+ popbox
+ }
+ }
+ if {$rlcov > 0.0} {
+ if {$grc == 1} {
+ pushbox
+ box move e ${hw}um
+ sky130::draw_contact 0 $ch $diff_surround $metal_surround \
+ $contact_size $plus_diff_type $plus_contact_type li vert
+ popbox
+ }
+ if {$glc == 1} {
+ pushbox
+ box move w ${hw}um
+ sky130::draw_contact 0 $ch $diff_surround $metal_surround \
+ $contact_size $plus_diff_type $plus_contact_type li vert
+ popbox
+ }
+ }
+
+ pushbox
+ box grow e ${hw}um
+ box grow w ${hw}um
+ box grow n ${hh}um
+ box grow s ${hh}um
+ # Create boundary using properties
+ property FIXED_BBOX [box values]
+ box grow c ${hx}um ;# to edge of contact
+ box grow c ${diff_surround}um ;# to edge of diffusion
+ box grow c ${sub_surround}um ;# sub/well overlap of diff (NOT guard_sub)
+ paint $sub_type
+ set cext [sky130::getbox]
+ popbox
+ popbox
+
+ return $cext
+}
+
+#----------------------------------------------------------------
+# MOSFET: Draw a single device
+#----------------------------------------------------------------
+
+proc sky130::mos_device {parameters} {
+
+ # Epsilon for avoiding round-off errors
+ set eps 0.0005
+
+ # Set local default values if they are not in parameters
+ set diffcov 100 ;# percent coverage of diffusion contact
+ set polycov 100 ;# percent coverage of poly contact
+ set topc 1 ;# draw top poly contact
+ set botc 1 ;# draw bottom poly contact
+ set evenodd 1 ;# even or odd numbered device finger, in X
+ set dev_sub_type "" ;# device substrate type (if different from guard ring)
+ 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 a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Draw the diffusion and poly
+ pushbox
+ box size 0 0
+ pushbox
+ set hw [/ $w 2.0]
+ set hl [/ $l 2.0]
+ set he [/ $min_effl 2.0]
+ if {$nf == 1 || $he < $hl} {set he $hl}
+ box grow n ${hw}um
+ box grow s ${hw}um
+ box grow e ${hl}um
+ box grow w ${hl}um
+ pushbox
+ if {${diff_extension} > ${gate_to_diffcont}} {
+ box grow e ${diff_extension}um
+ box grow w ${diff_extension}um
+ } else {
+ box grow e ${gate_to_diffcont}um
+ box grow w ${gate_to_diffcont}um
+ }
+ paint ${diff_type}
+ popbox
+ pushbox
+ if {${gate_extension} > ${gate_to_polycont}} {
+ box grow n ${gate_extension}um
+ box grow s ${gate_extension}um
+ } else {
+ if {$topc} {
+ box grow n ${gate_to_polycont}um
+ } else {
+ box grow n ${gate_extension}um
+ }
+ if {$botc} {
+ box grow s ${gate_to_polycont}um
+ } else {
+ box grow s ${gate_extension}um
+ }
+ }
+ paint ${poly_type}
+ set cext [sky130::getbox]
+ popbox
+ # save gate area now and paint later, so that diffusion surrounding the
+ # contact does not paint over the gate area, in case the gate type is
+ # not part of a "compose" entry in the techfile.
+ set gaterect [box values]
+ popbox
+
+ # Adjust position of contacts for dogbone geometry
+ # Rule 1: Minimize diffusion length. Contacts only move out
+ # if width < contact diffusion height. They move out enough
+ # that the diffusion-to-poly spacing is satisfied.
+
+ set ddover 0
+ set cdwmin [+ ${contact_size} [* ${diff_surround} 2]]
+ set cstem [- ${gate_to_diffcont} [/ ${cdwmin} 2.0]]
+ set cgrow [- ${diff_poly_space} ${cstem}]
+ if {[+ ${w} ${eps}] < ${cdwmin}} {
+ if {${cgrow} > 0} {
+ set gate_to_diffcont [+ ${gate_to_diffcont} ${cgrow}]
+ }
+ set ddover [/ [- ${cdwmin} ${w}] 2.0]
+ }
+
+ # Rule 2: Minimum poly width. Poly contacts only move out
+ # if length < contact poly width. They move out enough
+ # that the diffusion-to-poly spacing is satisfied.
+
+ set gporig ${gate_to_polycont}
+ set cplmin [+ ${contact_size} [* ${poly_surround} 2]]
+ set cstem [- ${gate_to_polycont} [/ ${cplmin} 2.0]]
+ set cgrow [- ${diff_poly_space} ${cstem}]
+ if {[+ ${l} ${eps}] < ${cplmin}} {
+ if {${cgrow} > 0} {
+ set gate_to_polycont [+ ${gate_to_polycont} ${cgrow}]
+ }
+ }
+
+ # Rule 3: If both poly and diffusion are dogboned, then move
+ # poly out further to clear spacing to the diffusion contact
+
+ if {[+ ${w} ${eps}] < ${cdwmin}} {
+ if {[+ ${l} ${eps}] < ${cplmin}} {
+ set cgrow [/ [- ${cplmin} ${w}] 2.0]
+ set gate_to_polycont [+ ${gate_to_polycont} ${cgrow}]
+ }
+ }
+
+ # Rule 4: If M > 1 and poly contacts overlap, then increase the
+ # transistor-to-poly-contact distance by the amount of any
+ # diffusion dogbone overhang.
+
+ if {($poverlap == 1) && ($m > 1)} {
+ if {${gate_to_polycont} - $gporig < $ddover} {
+ set gate_to_polycont [+ ${gporig} ${ddover}]
+ }
+ }
+
+ # Reduce contact sizes by poly or diffusion surround so that
+ # the contact area edges match the device diffusion or poly.
+ # (Minimum dimensions will be enforced by the contact drawing routine)
+ set tsurround [+ ${diff_surround} ${diff_overlap_cont}]
+ set cdw [- ${w} [* ${tsurround} 2]] ;# diff contact height
+ set cpl [- ${l} [* ${poly_surround} 2]] ;# poly contact width
+
+ # Reduce by coverage percentage. NOTE: If overlapping multiple devices,
+ # keep maximum poly contact coverage.
+
+ set cdw [* ${cdw} [/ ${diffcov} 100.0]]
+ if {($poverlap == 0) || ($m == 1)} {
+ set cpl [* ${cpl} [/ ${polycov} 100.0]]
+ }
+
+ # Right diffusion contact
+ pushbox
+ box move e ${he}um
+ box move e ${gate_to_diffcont}um
+ set cext [sky130::unionbox $cext [sky130::draw_contact 0 ${cdw} \
+ ${diff_surround} ${metal_surround} ${contact_size}\
+ ${diff_type} ${diff_contact_type} li vert]]
+ popbox
+ # Left diffusion contact
+ pushbox
+ box move w ${he}um
+ box move w ${gate_to_diffcont}um
+ set cext [sky130::unionbox $cext [sky130::draw_contact 0 ${cdw} \
+ ${diff_surround} ${metal_surround} ${contact_size} \
+ ${diff_type} ${diff_contact_type} li vert]]
+ set diffarea $cext
+ popbox
+ # Top poly contact
+ if {$topc} {
+ pushbox
+ box move n ${hw}um
+ box move n ${gate_to_polycont}um
+ set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
+ ${poly_surround} ${metal_surround} ${contact_size} \
+ ${poly_type} ${poly_contact_type} li horz]]
+ popbox
+ }
+ # Bottom poly contact
+ if {$botc} {
+ pushbox
+ box move s ${hw}um
+ box move s ${gate_to_polycont}um
+ set cext [sky130::unionbox $cext [sky130::draw_contact ${cpl} 0 \
+ ${poly_surround} ${metal_surround} ${contact_size} \
+ ${poly_type} ${poly_contact_type} li horz]]
+ popbox
+ }
+
+ # Now draw the gate, after contacts have been drawn
+ pushbox
+ box values {*}${gaterect}
+ # gate_type need not be defined if poly over diff paints the right type.
+ catch {paint ${gate_type}}
+ # sub_surround_dev, if defined, may create a larger area around the gate
+ # than sub_surround creates around the diffusion/poly area.
+ if [dict exists $parameters sub_surround_dev] {
+ box grow n ${sub_surround_dev}um
+ box grow s ${sub_surround_dev}um
+ box grow e ${sub_surround_dev}um
+ box grow w ${sub_surround_dev}um
+ paint ${dev_sub_type}
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ }
+ popbox
+
+ if {$dev_sub_type != ""} {
+ box values [lindex $diffarea 0]um [lindex $diffarea 1]um \
+ [lindex $diffarea 2]um [lindex $diffarea 3]um
+ box grow n ${sub_surround}um
+ box grow s ${sub_surround}um
+ box grow e ${sub_surround}um
+ box grow w ${sub_surround}um
+ paint ${dev_sub_type}
+ set cext [sky130::unionbox $cext [sky130::getbox]]
+ # puts stdout "Diagnostic: bounding box is $cext"
+ }
+
+ popbox
+ return $cext
+}
+
+#----------------------------------------------------------------
+# MOSFET: Draw the tiled device
+#----------------------------------------------------------------
+
+proc sky130::mos_draw {parameters} {
+ tech unlock *
+ set savesnap [snap]
+ snap internal
+
+ # Set defaults if they are not in parameters
+ set poverlap 0 ;# overlap poly contacts when tiling
+ set doverlap 1 ;# overlap diffusion contacts when tiling
+ set dev_sub_dist 0 ;# substrate to guard ring, if dev_sub_type defined
+ set dev_sub_space 0 ;# distance between substrate areas for arrayed devices
+ set min_allc 0 ;# gate length below which poly contacts must be interleaved
+ set id_type "" ;# additional type covering everything
+ 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 a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # 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
+ }
+
+ # If poverlap is 1 then both poly contacts must be present
+ if {$poverlap == 1} {
+ set topc 1
+ set botc 1
+ dict set parameters topc 1
+ dict set parameters botc 1
+ }
+
+ # Normalize distance units to microns
+ set w [magic::spice2float $w]
+ set l [magic::spice2float $l]
+
+ pushbox
+ 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
+
+ if {$nf > 1 && $l < $min_allc} {
+ set intc 1
+ set evenodd 1
+ set topc 1
+ set botc 1
+ dict set parameters topc 1
+ dict set parameters botc 1
+ set poverlap 0
+ } else {
+ set intc 0
+ }
+
+ # Determine the base device dimensions by drawing one device
+ # while all layers are locked (nothing drawn). This allows the
+ # base drawing routine to do complicated geometry without having
+ # to duplicate it here with calculations.
+
+ tech lock *
+ set bbox [sky130::mos_device $parameters]
+ # puts stdout "Diagnostic: Device bounding box e $bbox (um)"
+ tech unlock *
+
+ set fw [- [lindex $bbox 2] [lindex $bbox 0]]
+ set fh [- [lindex $bbox 3] [lindex $bbox 1]]
+ set lw [+ [lindex $bbox 2] [lindex $bbox 0]]
+ set lh [+ [lindex $bbox 3] [lindex $bbox 1]]
+
+ # 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.
+
+ if {$dev_sub_dist > 0} {
+ set poverlap 0
+ set doverlap 0
+
+ if {$dev_sub_space > $poly_spacing} {
+ set dx [+ $fw $dev_sub_space]
+ set dy [+ $fh $dev_sub_space]
+ } else {
+ set dx [+ $fw $poly_spacing]
+ set dy [+ $fh $poly_spacing]
+ }
+
+ } else {
+
+ # Determine tile width and height (depends on overlap)
+ if {$poverlap == 0} {
+ set dy [+ $fh $poly_spacing]
+ } else {
+ # overlap poly
+ set dy [- $fh [+ $poly_surround $poly_surround $contact_size]]
+ }
+
+ if {$doverlap == 0} {
+ set dx [+ $fw $diff_spacing]
+ } else {
+ # overlap diffusions
+ set dx [- $fw [+ $diff_surround $diff_surround $contact_size]]
+ }
+ }
+
+ # Determine core width and height
+ set corex [+ [* [- $nf 1] $dx] $fw]
+ set corey [+ [* [- $m 1] $dy] $fh]
+ set corellx [/ [+ [- $corex $fw] $lw] 2.0]
+ set corelly [/ [+ [- $corey $fh] $lh] 2.0]
+
+ # If there is a diffusion dogbone, and no top poly contact, then
+ # increase the core height by the amount of the dogbone overhang.
+
+ if {$topc == 0} {
+ set cdwmin [+ ${contact_size} [* ${diff_surround} 2]]
+ if {${w} < ${cdwmin}} {
+ set corey [+ $corey [/ [- ${cdwmin} ${w}] 2.0]]
+ }
+ }
+
+ if {$guard != 0} {
+ # Calculate guard ring size (measured to contact center)
+ if {($dev_sub_dist > 0) && ([+ $dev_sub_dist $sub_surround] > $diff_tap_space)} {
+ set gx [+ $corex [* 2.0 [+ $dev_sub_dist $diff_surround]] $contact_size]
+ } else {
+ set gx [+ $corex [* 2.0 [+ $diff_tap_space $diff_surround]] $contact_size]
+ }
+ if {($dev_sub_dist > 0) && ([+ $dev_sub_dist $sub_surround] > $diff_gate_space)} {
+ set gy [+ $corey [* 2.0 [+ $dev_sub_dist $diff_surround]] $contact_size]
+ } else {
+ set gy [+ $corey [* 2.0 [+ $diff_gate_space $diff_surround]] $contact_size]
+ }
+
+ # Somewhat tricky. . . if the width is small and the diffusion is
+ # a dogbone, and the top or bottom poly contact is missing, then
+ # the spacing to the guard ring may be limited by diffusion spacing, not
+ # poly to diffusion.
+
+ set inset [/ [+ $contact_size [* 2.0 $diff_surround] -$w] 2.0]
+ set sdiff [- [+ $inset $diff_tap_space] [+ $gate_extension $diff_gate_space]]
+
+ if {$sdiff > 0} {
+ if {$topc == 0} {
+ set gy [+ $gy $sdiff]
+ set corelly [+ $corelly [/ $sdiff 2.0]]
+ }
+ if {$botc == 0} {
+ set gy [+ $gy $sdiff]
+ set corelly [- $corelly [/ $sdiff 2.0]]
+ }
+ }
+
+ # Draw the guard ring first, as MOS well may interact with guard ring substrate
+ sky130::guard_ring $gx $gy $parameters
+ }
+
+ pushbox
+ # If any surrounding identifier type is defined, draw it
+ if {${id_type} != ""} {
+ set hw [/ $gx 2]
+ set hh [/ $gy 2]
+ box grow e ${hw}um
+ box grow w ${hw}um
+ box grow n ${hh}um
+ box grow s ${hh}um
+ box grow c ${id_surround}um
+ paint ${id_type}
+ }
+ popbox
+ pushbox
+ box move w ${corellx}um
+ box move s ${corelly}um
+ for {set xp 0} {$xp < $nf} {incr xp} {
+ pushbox
+ if {$intc == 1} {
+ set evenodd [- 1 $evenodd]
+ if {$evenodd == 1} {
+ dict set parameters topc 1
+ dict set parameters botc 0
+ } else {
+ dict set parameters topc 0
+ dict set parameters botc 1
+ }
+ set saveeo $evenodd
+ }
+ for {set yp 0} {$yp < $m} {incr yp} {
+ sky130::mos_device $parameters
+ box move n ${dy}um
+ if {$intc == 1} {
+ set evenodd [- 1 $evenodd]
+ if {$evenodd == 1} {
+ dict set parameters topc 1
+ dict set parameters botc 0
+ } else {
+ dict set parameters topc 0
+ dict set parameters botc 1
+ }
+ }
+ }
+ if {$intc == 1} {
+ set evenodd $saveeo
+ }
+ popbox
+ box move e ${dx}um
+ }
+ popbox
+ popbox
+
+ snap $savesnap
+ tech revert
+}
+
+#-------------------
+# nMOS 1.8V
+#-------------------
+
+proc sky130::nshort_draw {parameters} {
+ set newdict [dict create \
+ gate_type nfet \
+ diff_type ndiff \
+ diff_contact_type ndc \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ min_effl 0.185 \
+ min_allc 0.26 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+proc sky130::nlowvt_draw {parameters} {
+ set newdict [dict create \
+ gate_type nfetlvt \
+ diff_type ndiff \
+ diff_contact_type ndc \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ min_effl 0.185 \
+ min_allc 0.26 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+proc sky130::sonos_e_draw {parameters} {
+ set newdict [dict create \
+ gate_type nsonos \
+ diff_type ndiff \
+ diff_contact_type ndc \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ id_type dnwell \
+ id_surround 1.355 \
+ min_effl 0.185 \
+ min_allc 0.26 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+#-------------------
+# pMOS 1.8V
+#-------------------
+
+proc sky130::pshort_draw {parameters} {
+ set newdict [dict create \
+ gate_type pfet \
+ diff_type pdiff \
+ diff_contact_type pdc \
+ plus_diff_type nsd \
+ plus_contact_type nsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type nwell \
+ gate_to_polycont 0.32 \
+ min_effl 0.185 \
+ min_allc 0.26 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+proc sky130::plowvt_draw {parameters} {
+ set newdict [dict create \
+ gate_type pfetlvt \
+ diff_type pdiff \
+ diff_contact_type pdc \
+ plus_diff_type nsd \
+ plus_contact_type nsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type nwell \
+ gate_to_polycont 0.32 \
+ min_effl 0.185 \
+ min_allc 0.26 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+proc sky130::phighvt_draw {parameters} {
+ set newdict [dict create \
+ gate_type pfethvt \
+ diff_type pdiff \
+ diff_contact_type pdc \
+ plus_diff_type nsd \
+ plus_contact_type nsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type nwell \
+ gate_to_polycont 0.32 \
+ min_effl 0.185 \
+ min_allc 0.26 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+#-------------------
+# pMOS 5.0V
+#-------------------
+
+proc sky130::phv_draw {parameters} {
+ set newdict [dict create \
+ gate_type mvpfet \
+ diff_type mvpdiff \
+ diff_contact_type mvpdc \
+ plus_diff_type mvnsd \
+ plus_contact_type mvnsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type nwell \
+ guard_sub_surround 0.33 \
+ gate_to_polycont 0.32 \
+ diff_spacing 0.31 \
+ diff_tap_space 0.38 \
+ diff_gate_space 0.38 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+#-------------------
+# nMOS 5.0V
+#-------------------
+
+proc sky130::nhv_draw {parameters} {
+ set newdict [dict create \
+ gate_type mvnfet \
+ diff_type mvndiff \
+ diff_contact_type mvndc \
+ plus_diff_type mvpsd \
+ plus_contact_type mvpsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ diff_spacing 0.31 \
+ diff_tap_space 0.38 \
+ diff_gate_space 0.38 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+proc sky130::nhvnative_draw {parameters} {
+ set newdict [dict create \
+ gate_type mvnnfet \
+ diff_type mvndiff \
+ diff_contact_type mvndc \
+ plus_diff_type mvpsd \
+ plus_contact_type mvpsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ diff_spacing 0.30 \
+ diff_tap_space 0.38 \
+ diff_gate_space 0.38 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+#------------------------
+# MOS varactor (1.8V)
+#------------------------
+
+proc sky130::xcnwvc_draw {parameters} {
+ set newdict [dict create \
+ gate_type var \
+ diff_type nnd \
+ diff_contact_type nsc \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ dev_sub_type nwell \
+ diff_overlap_cont 0.06 \
+ dev_sub_dist 0.14 \
+ dev_sub_space 1.27 \
+ gate_to_diffcont 0.34 \
+ diff_extension 0.485 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+proc sky130::xcnwvc2_draw {parameters} {
+ set newdict [dict create \
+ gate_type varhvt \
+ diff_type nnd \
+ diff_contact_type nsc \
+ plus_diff_type psd \
+ plus_contact_type psc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ dev_sub_type nwell \
+ diff_overlap_cont 0.06 \
+ dev_sub_dist 0.14 \
+ dev_sub_space 1.27 \
+ gate_to_diffcont 0.34 \
+ diff_extension 0.485 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+#---------------------------------------------------------
+# MOS varactor (5.0V)
+# NOTE: dev_sub_space set to 2.0 assuming different nets.
+# Should have option for same-net with merged wells.
+#---------------------------------------------------------
+
+proc sky130::xchvnwc_draw {parameters} {
+ set newdict [dict create \
+ gate_type mvvar \
+ diff_type mvnsd \
+ diff_contact_type mvnsc \
+ plus_diff_type mvpsd \
+ plus_contact_type mvpsc \
+ poly_type poly \
+ poly_contact_type pc \
+ sub_type psub \
+ dev_sub_type nwell \
+ sub_surround 0.38 \
+ sub_surround_dev 0.56 \
+ guard_sub_surround 0.18 \
+ diff_overlap_cont 0.06 \
+ dev_sub_dist 0.785 \
+ dev_sub_space 2.0 \
+ gate_to_diffcont 0.34 \
+ diff_extension 0.485 \
+ ]
+ set drawdict [dict merge $sky130::ruleset $newdict $parameters]
+ return [sky130::mos_draw $drawdict]
+}
+
+#----------------------------------------------------------------
+# MOSFET: Check device parameters for out-of-bounds values
+#----------------------------------------------------------------
+
+proc sky130::mos_check {device parameters} {
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set l [magic::spice2float $l]
+ set l [magic::3digitpastdecimal $l]
+ set w [magic::spice2float $w]
+ set w [magic::3digitpastdecimal $w]
+
+ # nf, m must be integer
+ if {![string is int $nf]} {
+ puts stderr "NF must be an integer!"
+ dict set parameters nf 1
+ }
+ if {![string is int $m]} {
+ puts stderr "M must be an integer!"
+ dict set parameters m 1
+ }
+ # diffcov, polycov must be numeric
+ if {[catch {expr abs($diffcov)}]} {
+ puts stderr "diffcov must be numeric!"
+ set diffcov 100
+ }
+ if {[catch {expr abs($polycov)}]} {
+ puts stderr "polycov must be numeric!"
+ set polycov 100
+ }
+
+ if {$l < $lmin} {
+ puts stderr "Mos length must be >= $lmin um"
+ dict set parameters l $lmin
+ }
+ if {$w < $wmin} {
+ puts stderr "Mos width must be >= $wmin um"
+ dict set parameters w $wmin
+ }
+ if {$nf < 1} {
+ puts stderr "NF must be >= 1"
+ dict set parameters nf 1
+ }
+ if {$m < 1} {
+ puts stderr "M must be >= 1"
+ dict set parameters m 1
+ }
+ if {$diffcov < 20 } {
+ puts stderr "Diffusion contact coverage must be at least 20%"
+ dict set parameters diffcov 20
+ } elseif {$diffcov > 100 } {
+ puts stderr "Diffusion contact coverage can't be more than 100%"
+ dict set parameters diffcov 100
+ }
+ if {$polycov < 20 } {
+ puts stderr "Poly contact coverage must be at least 20%"
+ dict set parameters polycov 20
+ } elseif {$polycov > 100 } {
+ puts stderr "Poly contact coverage can't be more than 100%"
+ dict set parameters polycov 100
+ }
+
+ # Values must satisfy diffusion-to-tap spacing of 20um.
+ # Therefore the maximum of guard ring width or height cannot exceed 40um.
+ # If in violation, reduce counts first, as these are easiest to recover
+ # by duplicating the device and overlapping the wells.
+ set origm $m
+ set orignf $nf
+ while true {
+ set yext [expr ($w + 3.0) * $m]
+ set xext [expr ($l + 1.0) * $nf + 1.1]
+ if {[expr min($xext, $yext)] > 40.0} {
+ if {$yext > 40.0 && $m > 1} {
+ incr m -1
+ } elseif {$xext > 40.0 && $nf > 1} {
+ incr nf -1
+ } elseif {$yext > 40.0} {
+ set w 37
+ puts -nonewline stderr "Transistor width must be < 37 um"
+ puts stderr " to avoid tap spacing violation."
+ dict set parameters w $w
+ } elseif {$xext > 40.0} {
+ set l 37.9
+ puts -nonewline stderr "Transistor length must be < 37.9 um"
+ puts stderr " to avoid tap spacing violation."
+ dict set parameters l $l
+ }
+ } else {
+ break
+ }
+ }
+ if {$m != $origm} {
+ puts stderr "Y repeat reduced to prevent tap distance violation"
+ dict set parameters m $m
+ }
+ if {$nf != $orignf} {
+ puts stderr "X repeat reduced to prevent tap distance violation"
+ dict set parameters nf $nf
+ }
+
+ return $parameters
+}
+
+#----------------------------------------------------------------
+
+proc sky130::nshort_check {parameters} {
+ return [sky130::mos_check nshort $parameters]
+}
+
+proc sky130::nlowvt_check {parameters} {
+ return [sky130::mos_check nlowvt $parameters]
+}
+
+proc sky130::sonos_e_check {parameters} {
+ return [sky130::mos_check sonos_e $parameters]
+}
+
+proc sky130::nhv_check {parameters} {
+ return [sky130::mos_check nhv $parameters]
+}
+
+proc sky130::nhvnative_check {parameters} {
+ return [sky130::mos_check nhvnative $parameters]
+}
+
+proc sky130::pshort_check {parameters} {
+ return [sky130::mos_check pshort $parameters]
+}
+
+proc sky130::plowvt_check {parameters} {
+ return [sky130::mos_check plowvt $parameters]
+}
+
+proc sky130::phighvt_check {parameters} {
+ return [sky130::mos_check phighvt $parameters]
+}
+
+proc sky130::phv_check {parameters} {
+ return [sky130::mos_check phv $parameters]
+}
+
+proc sky130::xcnwvc_check {parameters} {
+ return [sky130::mos_check xcnwvc $parameters]
+}
+
+proc sky130::xcnwvc2_check {parameters} {
+ return [sky130::mos_check xcnwvc2 $parameters]
+}
+
+proc sky130::xchvnwc_check {parameters} {
+ return [sky130::mos_check xchvnwc $parameters]
+}
+
+#----------------------------------------------------------------
+# Fixed device: Specify all user-editable default values
+#
+# deltax --- Additional horizontal space between devices
+# deltay --- Additional vertical space between devices
+# nx --- Number of arrayed devices in X
+# ny --- Number of arrayed devices in Y
+#
+# Note that these values, specifically nx, ny, deltax,
+# and deltay, are properties of the instance, not the cell.
+# They translate to the instance array x and y counts; while
+# deltax is the x pitch less the cell width, and deltay is the
+# y pitch less the cell height.
+#
+# non-user-editable
+#
+# nocell --- Indicates that this cell has a predefined layout
+# and therefore there is no cell to draw.
+# xstep --- Width of the cell (nominal array pitch in X)
+# ystep --- Height of the cell (nominal array pitch in Y)
+#----------------------------------------------------------------
+
+# Fixed-layout devices (from sky130_fd_pr_base, _rf, and _rf2 libraries)
+#
+# Bipolar transistors:
+#
+# sky130_fd_pr_rf_npn_1x1
+# sky130_fd_pr_rf_npn_1x2
+# sky130_fd_pr_rf_pnp5x
+#
+# Parallel Plate Capacitors:
+#
+# sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield
+# sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield
+# sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield
+# sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield
+# sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield
+# sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield
+# sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield
+# sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield
+# sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield
+# sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4
+# sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield
+# sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield
+# sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield
+# sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield
+# sky130_fd_pr_rf_xcmvpp2
+# sky130_fd_pr_rf_xcmvpp2_nwell
+# sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield
+# sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield
+# sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield
+# sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield
+#
+# Inductors:
+#
+# balun
+# xind4_011
+# xind4_02
+
+proc sky130::sky130_fd_pr_rf_npn_1x1_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 7.03 ystep 7.03}
+}
+
+proc sky130::sky130_fd_pr_rf_npn_1x2_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 7.03 ystep 8.03}
+}
+
+proc sky130::sky130_fd_pr_rf_pnp5x_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 6.44 ystep 6.44}
+}
+
+proc sky130::balun_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 292 ystep 292}
+}
+
+proc sky130::xind4_02_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 258 ystep 258}
+}
+
+proc sky130::xind4_011_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 290 ystep 404}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 4.05 ystep 4.26}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 6.47 ystep 5.76}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 6.47 ystep 5.76}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 8.25 ystep 7.51}
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 10.41 ystep 11.54}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 11.08 ystep 11.36}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 1.77 ystep 1.77}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 3.88 ystep 3.88}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 4.55 ystep 4.76}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_nwell_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 4.55 ystep 4.76}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 4.05 ystep 4.26}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 4.05 ystep 4.26}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 8.25 ystep 7.51}
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield_defaults {} {
+ return {nx 1 ny 1 deltax 0 deltay 0 nocell 1 xstep 8.25 ystep 7.51}
+}
+
+#----------------------------------------------------------------
+# Fixed device: Conversion from SPICE netlist parameters to toolkit
+#----------------------------------------------------------------
+
+proc sky130::fixed_convert {parameters} {
+ set pdkparams [dict create]
+ dict for {key value} $parameters {
+ switch -nocase $key {
+ m {
+ dict set pdkparams nx $value
+ }
+ }
+ }
+ return $pdkparams
+}
+
+#----------------------------------------------------------------
+
+proc sky130::sky130_fd_pr_rf_npn_1x1_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_npn_1x2_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_pnp5x_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::balun_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::xind4_011_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::xind4_02_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_nwell_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield_convert {parameters} {
+ return [sky130::fixed_convert $parameters]
+}
+
+#----------------------------------------------------------------
+# Fixed device: Interactively specifies the fixed layout parameters
+#----------------------------------------------------------------
+
+proc sky130::fixed_dialog {parameters} {
+ # Instance fields: nx, ny, pitchx, pitchy
+ # Editable fields: nx, ny, deltax, deltay
+ # Non-editable fields: nocell, xstep, ystep
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # "nocell" field causes nx and ny to be dropped in from
+ # "array count". Also "pitchx" and "pitchy" are passed
+ # in internal units. Convert these to microns and generate
+ # If there is no pitchx and pitchy, then the device has not
+ # yet been created, so keep the deltax and deltay defaults.
+
+ if [dict exists $parameters pitchx] {
+ set pitchux [magic::i2u $pitchx]
+ set stepux [magic::spice2float $xstep]
+ set deltax [magic::3digitpastdecimal [expr $pitchux - $stepux]]
+ # An array size 1 should not cause deltax to go negative
+ if {$deltax < 0.0} {set deltax 0.0}
+ dict set parameters deltax $deltax
+ }
+ if [dict exists $parameters pitchy] {
+ set pitchuy [magic::i2u $pitchy]
+ set stepuy [magic::spice2float $ystep]
+ set deltay [magic::3digitpastdecimal [expr $pitchuy - $stepuy]]
+ # An array size 1 should not cause deltay to go negative
+ if {$deltay < 0.0} {set deltay 0.0}
+ dict set parameters deltay $deltay
+ }
+
+ magic::add_entry nx "NX" $parameters
+ magic::add_entry ny "NY" $parameters
+ magic::add_entry deltax "X step (um)" $parameters
+ magic::add_entry deltay "Y step (um)" $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_npn_1x1_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_npn_1x2_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_pnp5x_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::balun_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::xind4_011_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::xind4_02_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_nwell_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield_dialog {parameters} {
+ sky130::fixed_dialog $parameters
+}
+
+#----------------------------------------------------------------
+# Fixed device: Draw the device
+#----------------------------------------------------------------
+
+proc sky130::fixed_draw {devname parameters} {
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # This cell declares "nocell" in parameters, so it needs to
+ # instance the cell and set properties.
+
+ # Instantiate the cell. The name corresponds to the cell in the sky130_fd_pr_* directory.
+ set instname [getcell ${devname}]
+
+ set deltax [magic::spice2float $deltax]
+ set deltay [magic::spice2float $deltay]
+ set xstep [magic::spice2float $xstep]
+ set ystep [magic::spice2float $ystep]
+
+ # Array stepping
+ if {$nx > 1 || $ny > 1} {
+ set xstep [expr $xstep + $deltax]
+ set ystep [expr $ystep + $deltay]
+ box size ${xstep}um ${ystep}um
+ array $nx $ny
+ }
+ select cell $instname
+ expand
+ return $instname
+}
+
+#----------------------------------------------------------------
+# No additional parameters declared for drawing
+#----------------------------------------------------------------
+
+proc sky130::sky130_fd_pr_rf_npn_1x1_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_npn_1x1 $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_npn_1x2_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_npn_1x2 $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_pnp5x_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_pnp5x $parameters]
+}
+
+proc sky130::balun_draw {parameters} {
+ return [sky130::fixed_draw balun $parameters]
+}
+
+proc sky130::xind4_011_draw {parameters} {
+ return [sky130::fixed_draw xind4_011 $parameters]
+}
+
+proc sky130::xind4_02_draw {parameters} {
+ return [sky130::fixed_draw xind4_02 $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4 $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp2 $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_nwell_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp2_nwell $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield_draw {parameters} {
+ return [sky130::fixed_draw sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield $parameters]
+}
+
+#----------------------------------------------------------------
+# Fixed device: Check device parameters for out-of-bounds values
+#----------------------------------------------------------------
+
+proc sky130::fixed_check {parameters} {
+
+ # Set a local variable for each parameter (e.g., $l, $w, etc.)
+ foreach key [dict keys $parameters] {
+ set $key [dict get $parameters $key]
+ }
+
+ # Normalize distance units to microns
+ set deltax [magic::spice2float $deltax -1]
+ set deltax [magic::3digitpastdecimal $deltax]
+ set deltay [magic::spice2float $deltay -1]
+ set deltay [magic::3digitpastdecimal $deltay]
+
+ # nx, ny must be integer
+ if {![string is int $nx]} {
+ puts stderr "NX must be an integer!"
+ dict set parameters nx 1
+ }
+ if {![string is int $ny]} {
+ puts stderr "NY must be an integer!"
+ dict set parameters nx 1
+ }
+
+ # Number of devices in X and Y must be at least 1
+ if {$nx < 1} {
+ puts stderr "NX must be >= 1"
+ dict set parameters nx 1
+ }
+ if {$ny < 1} {
+ puts stderr "NY must be >= 1"
+ dict set parameters nx 1
+ }
+ # Step less than zero violates DRC
+ if {$deltax < 0} {
+ puts stderr "X step must be >= 0"
+ dict set parameters deltax 0
+ }
+ if {$deltay < 0} {
+ puts stderr "Y step must be >= 0"
+ dict set parameters deltay 0
+ }
+ return $parameters
+}
+
+#----------------------------------------------------------------
+
+proc sky130::sky130_fd_pr_rf_npn_1x1_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_npn_1x2_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_pnp5x_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::balun_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::xind4_011_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::xind4_02_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_lim5shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m3_lim5shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_m4shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym4shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp11p5x11p7_polym50p4shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp4p4x4p6_m3_lim5shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_lim4shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp6p8x6p1_polym4shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvpp8p6x7p9_m3_lim5shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf2_xcmvppx4_2xnhvnative10x4_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3_lishield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp11p5x11p7_m3shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_lishield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp1p8x1p8_m3shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp2_nwell_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3_lishield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp4p4x4p6_m3shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3_lishield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+
+proc sky130::sky130_fd_pr_rf_xcmvpp8p6x7p9_m3shield_check {parameters} {
+ return [sky130::fixed_check $parameters]
+}
+