| #----------------------------------------------------- |
| # Magic/TCL design kit for SKYWATER sky130A |
| #----------------------------------------------------- |
| # Tim Edwards |
| # Revision 1 ALPHA 9/10/2020 |
| #----------------------------------------------------- |
| |
| if {[catch {set TECHPATH $env(PDK_ROOT)}]} { |
| set TECHPATH /foss/pdk |
| } |
| if [catch {set PDKPATH}] {set PDKPATH ${TECHPATH}/sky130A} |
| set PDKNAME sky130A |
| # "sky130" is the namespace used for all devices |
| set PDKNAMESPACE sky130 |
| puts stdout "Loading sky130A 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) 0 |
| |
| # 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::sky130_fd_pr__nfet_01v8" pdk1 |
| magic::add_toolkit_command $layoutframe "pmos (MOSFET)" \ |
| "magic::gencell sky130::sky130_fd_pr__pfet_01v8" pdk1 |
| |
| magic::add_toolkit_separator $layoutframe pdk1 |
| magic::add_toolkit_command $layoutframe "n-diode" \ |
| "magic::gencell sky130::sky130_fd_pr__diode_pw2nd_05v5" pdk1 |
| magic::add_toolkit_command $layoutframe "p-diode" \ |
| "magic::gencell sky130::sky130_fd_pr__diode_pd2nw_05v5" pdk1 |
| magic::add_toolkit_command $layoutframe "photodiode" \ |
| "magic::gencell sky130::sky130_fd_pr__photodiode" pdk1 |
| |
| magic::add_toolkit_separator $layoutframe pdk1 |
| magic::add_toolkit_command $layoutframe "MOS varactor" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_var_lvt" pdk1 |
| magic::add_toolkit_separator $layoutframe pdk1 |
| |
| magic::add_toolkit_command $layoutframe "NPN 1.0 x 1.0" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_npn_05v5_W1p00L1p00" pdk1 |
| magic::add_toolkit_command $layoutframe "NPN 1.0 x 2.0" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_npn_05v5_W1p00L2p00" pdk1 |
| magic::add_toolkit_command $layoutframe "PNP 0.68 x 0.68" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_pnp_05v5_W0p68L0p68" pdk1 |
| magic::add_toolkit_command $layoutframe "PNP 3.4 x 3.4" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_pnp_05v5_W3p40L3p40" pdk1 |
| |
| magic::add_toolkit_separator $layoutframe pdk1 |
| |
| magic::add_toolkit_command $layoutframe "inductor 1" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_test_coil1" pdk1 |
| magic::add_toolkit_command $layoutframe "inductor 2" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_test_coil2" pdk1 |
| magic::add_toolkit_command $layoutframe "inductor 3" \ |
| "magic::gencell sky130::sky130_fd_pr__rf_test_coil3" 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 |
| magic::add_toolkit_command $layoutframe "via3" \ |
| "sky130::via3_draw" pdk1 |
| magic::add_toolkit_command $layoutframe "via4" \ |
| "sky130::via4_draw" pdk1 |
| |
| |
| magic::add_toolkit_menu $layoutframe "Devices 2" pdk2 |
| |
| magic::add_toolkit_command $layoutframe "n-diff resistor (1.8V) - 120 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_nd" pdk2 |
| magic::add_toolkit_command $layoutframe "p-diff resistor (1.8V) - 197 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_pd" pdk2 |
| magic::add_toolkit_command $layoutframe "n-diff resistor (5.0V) - 114 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_nd__hv" pdk2 |
| magic::add_toolkit_command $layoutframe "p-diff resistor (5.0V) - 191 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_pd__hv" pdk2 |
| |
| magic::add_toolkit_command $layoutframe "poly resistor - 48.2 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_po" pdk2 |
| magic::add_toolkit_command $layoutframe "poly resistor - 319.8 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_high_po_0p35" pdk2 |
| magic::add_toolkit_command $layoutframe "poly resistor - 2000 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_xhigh_po_0p35" pdk2 |
| magic::add_toolkit_command $layoutframe "p-well resistor - 3050 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_iso_pw" pdk2 |
| magic::add_toolkit_separator $layoutframe pdk2 |
| |
| magic::add_toolkit_command $layoutframe "l1 metal resistor - 12.2 Ohm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_l1" pdk2 |
| magic::add_toolkit_command $layoutframe "m1 metal resistor - 125 mOhm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_m1" pdk2 |
| magic::add_toolkit_command $layoutframe "m2 metal resistor - 125 mOhm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_m2" pdk2 |
| magic::add_toolkit_command $layoutframe "m3 metal resistor - 47 mOhm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_m3" pdk2 |
| magic::add_toolkit_command $layoutframe "m4 metal resistor - 47 mOhm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_m4" pdk2 |
| magic::add_toolkit_command $layoutframe "m5 metal resistor - 29 mOhm/sq" \ |
| "magic::gencell sky130::sky130_fd_pr__res_generic_m5" pdk2 |
| |
| magic::add_toolkit_command $layoutframe "MiM cap - 2fF/um^2 (metal3)" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_mim_m3_1" pdk2 |
| magic::add_toolkit_command $layoutframe "MiM cap - 2fF/um^2 (metal4)" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_mim_m3_2" pdk2 |
| magic::add_toolkit_separator $layoutframe pdk2 |
| |
| magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 m1-m4, li/m5 shield" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5" pdk2 |
| magic::add_toolkit_command $layoutframe "vpp 11.5x11.7 m1-m2" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield" pdk2 |
| magic::add_toolkit_command $layoutframe "vpp 8.6x7.8 m1-m2 l1 shield" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_shieldl1" pdk2 |
| magic::add_toolkit_command $layoutframe "vpp 4.4x4.6 m1-m2 l1 shield" \ |
| "magic::gencell sky130::sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_shieldl1" pdk2 |
| |
| # Additional DRC style for routing only---add this to the DRC menu |
| ${layoutframe}.titlebar.mbuttons.drc.toolmenu add command -label "DRC Routing" -command {drc style drc(routing)} |
| |
| # Add SPICE import function to File menu |
| ${layoutframe}.titlebar.mbuttons.file.toolmenu insert 4 command -label "Import SPICE" -command {sky130::importspice} |
| ${layoutframe}.titlebar.mbuttons.file.toolmenu insert 4 separator |
| |
| # 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 |
| } |
| } |
| |
| #---------------------------------------------------------------- |
| # Menu callback function to read a SPICE netlist and generate an |
| # initial layout using the SKYWATER sky130A gencells. |
| #---------------------------------------------------------------- |
| |
| proc sky130::importspice {} { |
| global CAD_ROOT |
| |
| set Layoutfilename [ tk_getOpenFile -filetypes \ |
| {{SPICE {.spice .spc .spi .ckt .cir .sp \ |
| {.spice .spc .spi .ckt .cir .sp}}} {"All files" {*}}}] |
| if {$Layoutfilename != ""} { |
| magic::netlist_to_layout $Layoutfilename sky130 |
| } |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::mcon_draw {{dir default}} { |
| 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 |
| } |
| suspendall |
| paint mcon |
| pushbox |
| if {($w < $h) || ($dir == "vert")} { |
| box grow e 0.03um |
| box grow w 0.03um |
| box grow n 0.06um |
| box grow s 0.06um |
| paint m1 |
| } else { |
| box grow n 0.03um |
| box grow s 0.03um |
| box grow e 0.06um |
| box grow w 0.06um |
| paint m1 |
| } |
| popbox |
| resumeall |
| } |
| |
| 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 |
| } |
| suspendall |
| 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 |
| resumeall |
| } |
| |
| 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 |
| } |
| suspendall |
| pushbox |
| paint via2 |
| box grow n 0.05um |
| box grow s 0.05um |
| paint m2 |
| popbox |
| pushbox |
| box grow n 0.025um |
| box grow s 0.025um |
| box grow e 0.05um |
| box grow w 0.05um |
| paint m3 |
| popbox |
| resumeall |
| } |
| |
| 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 |
| } |
| suspendall |
| pushbox |
| paint via3 |
| box grow n 0.005um |
| box grow s 0.005um |
| box grow e 0.005um |
| box grow w 0.005um |
| paint m4 |
| popbox |
| pushbox |
| box grow e 0.05um |
| box grow w 0.05um |
| paint m3 |
| popbox |
| resumeall |
| } |
| |
| 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 |
| } |
| suspendall |
| paint via4 |
| pushbox |
| box grow n 0.12um |
| box grow s 0.12um |
| box grow e 0.12um |
| box grow w 0.12um |
| paint m5 |
| popbox |
| resumeall |
| } |
| |
| 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 |
| } |
| suspendall |
| paint psc |
| pushbox |
| if {$w > $h} { |
| box grow e 0.08um |
| box grow w 0.08um |
| paint li |
| box grow e 0.04um |
| box grow w 0.04um |
| } else { |
| box grow n 0.08um |
| box grow s 0.08um |
| paint li |
| box grow n 0.04um |
| box grow s 0.04um |
| } |
| paint psd |
| popbox |
| resumeall |
| } |
| |
| #---------------------------------------------------------------- |
| |
| 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 |
| } |
| suspendall |
| paint mvpsc |
| pushbox |
| if {$w > $h} { |
| box grow e 0.08um |
| box grow w 0.08um |
| paint li |
| box grow e 0.04um |
| box grow w 0.04um |
| } else { |
| box grow n 0.08um |
| box grow s 0.08um |
| paint li |
| box grow n 0.04um |
| box grow s 0.04um |
| } |
| paint mvpsd |
| popbox |
| resumeall |
| } |
| |
| #---------------------------------------------------------------- |
| |
| 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 |
| } |
| if {[dict exists $parameters viagb]} { |
| magic::add_entry viagb "Bottom guard ring via coverage \[+/-\](%)" $parameters |
| } |
| if {[dict exists $parameters viagt]} { |
| magic::add_entry viagt "Top guard ring via coverage \[+/-\](%)" $parameters |
| } |
| if {[dict exists $parameters viagr]} { |
| magic::add_entry viagr "Right guard ring via coverage \[+/-\](%)" $parameters |
| } |
| if {[dict exists $parameters viagl]} { |
| magic::add_entry viagl "Left guard ring via coverage \[+/-\](%)" $parameters |
| } |
| |
| if {[dict exists $parameters vias]} { |
| magic::add_checkbox vias "Add vias over contacts" $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 guard 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 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 |
| } |
| |
| # Check via coverage for syntax |
| if {$guard == 1} { |
| if {[catch {expr abs($viagb)}]} { |
| puts stderr "Guard ring bottom via coverage must be numeric!" |
| dict set parameters viagb 0 |
| } elseif {[expr abs($viagb)] > 100} { |
| puts stderr "Guard ring bottom via coverage can't be more than 100%" |
| dict set parameters viagb 100 |
| } |
| if {[catch {expr abs($viagt)}]} { |
| puts stderr "Guard ring top via coverage must be numeric!" |
| dict set parameters viagt 0 |
| } elseif {[expr abs($viagt)] > 100} { |
| puts stderr "Guard ring top via coverage can't be more than 100%" |
| dict set parameters viagt 100 |
| } |
| if {[catch {expr abs($viagr)}]} { |
| puts stderr "Guard ring right via coverage must be numeric!" |
| dict set parameters viagr 0 |
| } elseif {[expr abs($viagr)] > 100} { |
| puts stderr "Guard ring right via coverage can't be more than 100%" |
| dict set parameters viagr 100 |
| } |
| if {[catch {expr abs($viagl)}]} { |
| puts stderr "Guard ring left via coverage must be numeric!" |
| dict set parameters viagl 0 |
| } elseif {[expr abs($viagl)] > 100} { |
| puts stderr "Guard ring left via coverage can't be more than 100%" |
| dict set parameters viagl 100 |
| } |
| } |
| |
| # 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: sky130_fd_pr__diode_pw2nd_05v5_lvt, |
| # sky130_fd_pr__diode_pw2nd_05v5_nvt, sky130_fd_pr__diode_pd2nw_05v5_lvt, |
| # and sky130_fd_pr__diode_pd2nw_11v0 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::sky130_fd_pr__diode_pw2nd_05v5_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 {sky130_fd_pr__diode_pw2nd_05v5 sky130_fd_pr__diode_pw2nd_05v5_lvt \ |
| sky130_fd_pr__diode_pw2nd_05v5_nvt sky130_fd_pr__diode_pw2nd_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_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 \ |
| compatible {sky130_fd_pr__diode_pw2nd_05v5 sky130_fd_pr__diode_pw2nd_05v5_lvt \ |
| sky130_fd_pr__diode_pw2nd_05v5_nvt sky130_fd_pr__diode_pw2nd_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_nvt_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 {sky130_fd_pr__diode_pw2nd_05v5 sky130_fd_pr__diode_pw2nd_05v5_lvt \ |
| sky130_fd_pr__diode_pw2nd_05v5_nvt sky130_fd_pr__diode_pw2nd_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_11v0_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 {sky130_fd_pr__diode_pw2nd_05v5 sky130_fd_pr__diode_pw2nd_05v5_lvt \ |
| sky130_fd_pr__diode_pw2nd_05v5_nvt sky130_fd_pr__diode_pw2nd_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__photodiode_defaults {} { |
| return {nx 1 ny 1 deltax 0 deltay 0 xstep 8.0 ystep 8.0} |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_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 {sky130_fd_pr__diode_pd2nw_05v5 sky130_fd_pr__diode_pd2nw_05v5_lvt \ |
| sky130_fd_pr__diode_pd2nw_05v5_hvt sky130_fd_pr__diode_pd2nw_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_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 \ |
| compatible {sky130_fd_pr__diode_pd2nw_05v5 sky130_fd_pr__diode_pd2nw_05v5_lvt \ |
| sky130_fd_pr__diode_pd2nw_05v5_hvt sky130_fd_pr__diode_pd2nw_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_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 \ |
| compatible {sky130_fd_pr__diode_pd2nw_05v5 sky130_fd_pr__diode_pd2nw_05v5_lvt \ |
| sky130_fd_pr__diode_pd2nw_05v5_hvt sky130_fd_pr__diode_pd2nw_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_11v0_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 {sky130_fd_pr__diode_pd2nw_05v5 sky130_fd_pr__diode_pd2nw_05v5_lvt \ |
| sky130_fd_pr__diode_pd2nw_05v5_hvt sky130_fd_pr__diode_pd2nw_11v0} \ |
| full_metal 1 vias 1 viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_lvt_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_nvt_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_11v0_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__photodiode_convert {parameters} { |
| return [sky130::fixed_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_lvt_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_hvt_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_11v0_convert {parameters} { |
| return [sky130::diode_convert $parameters] |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pw2nd_05v5 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_lvt_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pw2nd_05v5_lvt $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_nvt_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pw2nd_05v5_nvt $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_11v0_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pw2nd_11v0 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__photodiode_dialog {parameters} { |
| sky130::fixed_dialog $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pd2nw_05v5 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_lvt_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pd2nw_05v5_lvt $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_hvt_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pd2nw_05v5_hvt $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_11v0_dialog {parameters} { |
| sky130::diode_dialog sky130_fd_pr__diode_pd2nw_11v0 $parameters |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_lvt_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_nvt_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_11v0_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__photodiode_check {parameters} { |
| sky130::fixed_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_lvt_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_05v5_hvt_check {parameters} { |
| sky130::diode_check $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__diode_pd2nw_11v0_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]] |
| |
| # Draw via over contact first |
| if {$vias != 0} { |
| pushbox |
| set ch $l |
| if {$ch < $via_size} {set ch $via_size} |
| set cw $w |
| if {$cw < $via_size} {set cw $via_size} |
| box grow n [/ $ch 2]um |
| box grow s [/ $ch 2]um |
| box grow w [/ $cw 2]um |
| box grow e [/ $cw 2]um |
| sky130::mcon_draw |
| popbox |
| } |
| 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 |
| } |
| |
| #---------------------------------------------------------------- |
| # Photodiode: Draw a single device |
| #---------------------------------------------------------------- |
| |
| proc sky130::photodiode_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] |
| } |
| |
| # Draw the device |
| pushbox |
| box size 0 0 |
| |
| # Device has ntap fixed width of 0.41 x 0.41 |
| # Surrounded by nwell 0.84 x 0.84 |
| # Surrounded by deep nwell 3.0 x 3.0 |
| |
| pushbox |
| box grow c 0.205um |
| paint nsd |
| popbox |
| pushbox |
| box grow c 0.42um |
| paint nwell |
| popbox |
| pushbox |
| box grow c 1.5um |
| paint photo |
| |
| set cext [sky130::getbox] |
| |
| popbox |
| |
| # Only enough space for one contact |
| set w ${contact_size} |
| set l ${contact_size} |
| |
| set cext [sky130::unionbox $cext [sky130::draw_contact ${w} ${l} \ |
| 0 ${metal_surround} ${contact_size} \ |
| nsd nsc li horz]] |
| |
| popbox |
| return $cext |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::photodiode_draw {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 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::photodiode_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 |
| |
| set dx [+ $fw $end_spacing] |
| set dy [+ $fh $end_spacing] |
| |
| # 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] |
| |
| # Calculate guard ring size (measured to contact center) |
| # Spacing between photodiode (deep nwell) and deep nwell (other) is 5.3um |
| set gx [+ $corex 15.965] |
| set gy [+ $corey 15.965] |
| |
| pushbox |
| |
| # The deep nwell is offset 0.315 from the nwell ring center to get the |
| # right overlap. The deep nwell ring has a minimum width of 3um. |
| set hgx [/ $gx 2.0] |
| set hgy [/ $gy 2.0] |
| set dwx [+ $hgx 0.315] |
| set dwy [+ $hgy 0.315] |
| box grow e ${dwx}um |
| box grow w ${dwx}um |
| box grow n ${dwy}um |
| box grow s ${dwy}um |
| paint dnwell |
| box grow e -3.0um |
| box grow w -3.0um |
| box grow n -3.0um |
| box grow s -3.0um |
| erase dnwell |
| |
| popbox |
| |
| # Draw the guard ring first. 0.63 is the amount nwell surrounds contact; |
| # 0.63 * 2 + 0.17 = total nwell width 1.43um, needed to cover dnwell edge. |
| set newdict [dict create \ |
| sub_type space \ |
| guard_sub_type nwell \ |
| guard_sub_surround 0.63 \ |
| plus_diff_type nsd \ |
| plus_contact_type nsc \ |
| ] |
| set guarddict [dict merge $parameters $newdict] |
| sky130::guard_ring $gx $gy $guarddict |
| |
| # Draw outside P-ring and generated the 2nd ring |
| set gx [+ $gx [* 2.0 [+ 0.56 $diff_spacing $diff_surround]] $contact_size] |
| set gy [+ $gy [* 2.0 [+ 0.56 $diff_spacing $diff_surround]] $contact_size] |
| sky130::guard_ring $gx $gy $parameters |
| |
| 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} { |
| sky130::photodiode_device $parameters |
| box move n ${dy}um |
| } |
| popbox |
| box move e ${dx}um |
| } |
| popbox |
| popbox |
| |
| tech revert |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_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 sky130_fd_pr__diode_pd2nw_05v5_lvt and |
| # sky130_fd_pr__diode_pd2nw_11v0). |
| |
| proc sky130::sky130_fd_pr__diode_pw2nd_05v5_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::sky130_fd_pr__diode_pw2nd_05v5_nvt_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::sky130_fd_pr__diode_pw2nd_11v0_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::sky130_fd_pr__diode_pd2nw_05v5_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::sky130_fd_pr__diode_pd2nw_05v5_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::sky130_fd_pr__diode_pd2nw_05v5_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::sky130_fd_pr__diode_pd2nw_11v0_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] |
| } |
| |
| #---------------------------------------------------------------- |
| # The photodiode has its own drawing routine, so |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__photodiode_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 \ |
| sub_type space \ |
| end_spacing 5.0 \ |
| end_surround 1.0 \ |
| sub_spacing 5.3 \ |
| guard_sub_type pwell \ |
| guard_sub_surround 0.18 \ |
| plus_diff_type psd \ |
| plus_contact_type psc \ |
| ] |
| set drawdict [dict merge $sky130::ruleset $newdict $parameters] |
| return [sky130::photodiode_draw $drawdict] |
| } |
| |
| #---------------------------------------------------------------- |
| # Drawn capacitor routines |
| # NOTE: Work in progress. These values need to be corrected. |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__cap_mim_m3_1_defaults {} { |
| return {w 2.00 l 2.00 val 8.0 carea 2.00 cperi 0.19 \ |
| 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 \ |
| ccov 100} |
| } |
| proc sky130::sky130_fd_pr__cap_mim_m3_2_defaults {} { |
| return {w 2.00 l 2.00 val 8.0 carea 2.00 cperi 0.19 \ |
| 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 \ |
| ccov 100} |
| } |
| |
| |
| #---------------------------------------------------------------- |
| # 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 |
| } |
| |
| proc sky130::sky130_fd_pr__cap_mim_m3_1_convert {parameters} { |
| return [cap_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__cap_mim_m3_2_convert {parameters} { |
| return [cap_convert $parameters] |
| } |
| |
| #---------------------------------------------------------------- |
| # 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 ccov]} { |
| magic::add_entry ccov "Capacitor contact coverage \[+/-\](%)" $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 |
| } |
| |
| proc sky130::sky130_fd_pr__cap_mim_m3_1_dialog {parameters} { |
| sky130::cap_dialog sky130_fd_pr__cap_mim_m3_1 $parameters |
| } |
| proc sky130::sky130_fd_pr__cap_mim_m3_2_dialog {parameters} { |
| sky130::cap_dialog sky130_fd_pr__cap_mim_m3_2 $parameters |
| } |
| |
| #---------------------------------------------------------------- |
| # 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 end_spacing 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 top_metal_width 0 ;# top metal minimum width |
| set contact_size 0 ;# cap contact minimum size |
| set ccov 100 ;# amount of contact coverage |
| |
| # 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 |
| |
| # Find contact width if ccov is other than 100 |
| set cmaxw [- $w [* $cap_surround 2]] |
| set cw [* $cmaxw [/ [expr abs($ccov)] 100.0]] |
| # Contact width must meet minimum |
| if {$cw < $contact_size} {set cw $contact_size} |
| if {$cw < $top_metal_width} {set cw $top_metal_width} |
| # Difference between maximum contact width and actual contact width |
| set cdif [- $cmaxw $cw] |
| |
| # Reduce the box to the maximum contact area |
| box grow n -${cap_surround}um |
| box grow s -${cap_surround}um |
| box grow e -${cap_surround}um |
| box grow w -${cap_surround}um |
| |
| set anchor [string index $ccov 0] |
| if {$anchor == "+"} { |
| box grow e -${cdif}um |
| } elseif {$anchor == "-"} { |
| box grow w -${cdif}um |
| } else { |
| set cdif [/ ${cdif} 2] |
| box grow w -${cdif}um |
| box grow e -${cdif}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]] |
| |
| # Calculate the distance from the top metal on the cap contact |
| # to the top metal on the end contact. |
| set top_met_sep [+ $end_spacing [- $cdif $top_surround]] |
| |
| # Diagnostic! |
| puts stdout "cdif = $cdif" |
| puts stdout "top_met_sep = $top_met_sep" |
| |
| # Increase end spacing if top metal spacing rule is not met |
| set loc_end_spacing $end_spacing |
| if {$top_met_sep < $top_metal_space} { |
| set loc_end_spacing [+ $loc_end_spacing [- $top_metal_space $top_met_sep]] |
| } |
| # Diagnostic! |
| puts stdout "loc_end_spacing = $loc_end_spacing" |
| |
| # Extend bottom metal under contact to right |
| box grow e ${loc_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 ${loc_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 |
| } |
| |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__cap_mim_m3_1_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.1 \ |
| contact_size 0.32 \ |
| metal_surround 0.08 \ |
| ] |
| set drawdict [dict merge $sky130::ruleset $newdict $parameters] |
| return [sky130::cap_draw $drawdict] |
| } |
| |
| proc sky130::sky130_fd_pr__cap_mim_m3_2_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 0.1 \ |
| 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] |
| } |
| |
| |
| #---------------------------------------------------------------- |
| # 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 ccov 100 |
| |
| # 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 |
| } |
| if {[catch {expr abs($ccov)}]} { |
| puts stderr "Capacitor contact coverage must be numeric!" |
| dict set parameters ccov 100 |
| } elseif {[expr abs($ccov)] > 100} { |
| puts stderr "Capaitor contact coverage can't be more than 100%" |
| dict set parameters ccov 100 |
| } |
| |
| # 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 |
| } |
| |
| proc sky130::sky130_fd_pr__cap_mim_m3_1_check {parameters} { |
| return [sky130::cap_check $parameters] |
| } |
| proc sky130::sky130_fd_pr__cap_mim_m3_2_check {parameters} { |
| return [sky130::cap_check $parameters] |
| } |
| |
| #---------------------------------------------------------------- |
| # 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 |
| #---------------------------------------------------------------- |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_iso_pw: Specify all user-editable default values and those |
| # needed by sky130_fd_pr__res_iso_pw_check |
| # NOTE: Work in progress. These values need to be corrected. |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_iso_pw_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 \ |
| guard 1 endcov 100 full_metal 1 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| #---------------------------------------------------------------- |
| # rpp1: Specify all user-editable default values and those |
| # needed by rp1_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_po_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 guard 1 \ |
| glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \ |
| full_metal 1 hv_guard 0 n_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| # "term" is rho * 0.06, the distance between xpc edge and CONT. |
| proc sky130::sky130_fd_pr__res_high_po_0p35_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 194.82 \ |
| sterm 0.0 caplen 0 guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_high_po_0p35 \ |
| sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \ |
| sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \ |
| full_metal 1 wmax 0.350 vias 1 n_guard 0 hv_guard 0 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_high_po_0p69_defaults {} { |
| return {w 0.690 l 1.00 m 1 nx 1 wmin 0.690 lmin 0.50 \ |
| rho 319.8 val 463.480 dummy 0 dw 0.0 term 194.82 \ |
| sterm 0.0 caplen 0 guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_high_po_0p35 \ |
| sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \ |
| sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \ |
| full_metal 1 wmax 0.690 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_high_po_1p41_defaults {} { |
| return {w 1.410 l 2.00 m 1 nx 1 wmin 1.410 lmin 0.50 \ |
| rho 319.8 val 453.620 dummy 0 dw 0.0 term 194.82 \ |
| sterm 0.0 caplen 0 guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_high_po_0p35 \ |
| sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \ |
| sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \ |
| full_metal 1 wmax 1.410 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_high_po_2p85_defaults {} { |
| return {w 2.850 l 3.00 m 1 nx 1 wmin 2.850 lmin 0.50 \ |
| rho 319.8 val 336.630 dummy 0 dw 0.0 term 194.82 \ |
| sterm 0.0 caplen 0 guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_high_po_0p35 \ |
| sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \ |
| sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \ |
| full_metal 1 wmax 2.850 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_high_po_5p73_defaults {} { |
| return {w 5.730 l 6.00 m 1 nx 1 wmin 5.730 lmin 0.50 \ |
| rho 319.8 val 334.870 dummy 0 dw 0.0 term 194.82 \ |
| sterm 0.0 caplen 0 guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_high_po_0p35 \ |
| sky130_fd_pr__res_high_po_0p69 sky130_fd_pr__res_high_po_1p41 \ |
| sky130_fd_pr__res_high_po_2p85 sky130_fd_pr__res_high_po_5p73} \ |
| full_metal 1 wmax 5.730 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| # "term" is rho * 0.06, the distance between xpc edge and CONT. |
| proc sky130::sky130_fd_pr__res_xhigh_po_0p35_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 188.2 \ |
| sterm 0.0 caplen 0 wmax 0.350 \ |
| guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_xhigh_po_0p35 \ |
| sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \ |
| sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \ |
| full_metal 1 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_0p69_defaults {} { |
| return {w 0.690 l 1.00 m 1 nx 1 wmin 0.690 lmin 0.50 \ |
| rho 2000 val 2898.600 dummy 0 dw 0.0 term 188.2 \ |
| sterm 0.0 caplen 0 wmax 0.690 \ |
| guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_xhigh_po_0p35 \ |
| sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \ |
| sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \ |
| full_metal 1 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_1p41_defaults {} { |
| return {w 1.410 l 2.00 m 1 nx 1 wmin 1.410 lmin 0.50 \ |
| rho 2000 val 2836.900 dummy 0 dw 0.0 term 188.2 \ |
| sterm 0.0 caplen 0 wmax 1.410 \ |
| guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_xhigh_po_0p35 \ |
| sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \ |
| sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \ |
| full_metal 1 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_2p85_defaults {} { |
| return {w 2.850 l 3.00 m 1 nx 1 wmin 2.850 lmin 0.50 \ |
| rho 2000 val 2105.300 dummy 0 dw 0.0 term 188.2 \ |
| sterm 0.0 caplen 0 wmax 2.850 \ |
| guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_xhigh_po_0p35 \ |
| sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \ |
| sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \ |
| full_metal 1 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_5p73_defaults {} { |
| return {w 5.730 l 6.00 m 1 nx 1 wmin 5.730 lmin 0.50 \ |
| rho 2000 val 2094.200 dummy 0 dw 0.0 term 188.2 \ |
| sterm 0.0 caplen 0 wmax 5.730 \ |
| guard 1 glc 1 grc 1 gtc 1 gbc 1 \ |
| compatible {sky130_fd_pr__res_xhigh_po_0p35 \ |
| sky130_fd_pr__res_xhigh_po_0p69 sky130_fd_pr__res_xhigh_po_1p41 \ |
| sky130_fd_pr__res_xhigh_po_2p85 sky130_fd_pr__res_xhigh_po_5p73} \ |
| full_metal 1 n_guard 0 hv_guard 0 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_generic_nd: Specify all user-editable default values and those |
| # needed by rdn_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_nd_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 guard 1 \ |
| glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \ |
| full_metal 1 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_nd__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 guard 1 \ |
| glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \ |
| full_metal 1 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_generic_pd: Specify all user-editable default values and those |
| # needed by rdp_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_pd_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 guard 1 \ |
| glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \ |
| full_metal 1 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_pd__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 guard 1 \ |
| glc 1 grc 1 gtc 1 gbc 1 roverlap 0 endcov 100 \ |
| full_metal 1 vias 1 \ |
| viagb 0 viagt 0 viagl 0 viagr 0} |
| } |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_generic_l1: Specify all user-editable default values and those needed |
| # by sky130_fd_pr__res_generic_l1_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_l1_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} |
| } |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_generic_m1: Specify all user-editable default values and those needed |
| # by sky130_fd_pr__res_generic_m1_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_m1_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} |
| } |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_generic_m2: Specify all user-editable default values and those needed |
| # by sky130_fd_pr__res_generic_m2_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_m2_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} |
| } |
| |
| #---------------------------------------------------------------- |
| # sky130_fd_pr__res_generic_m3: Specify all user-editable default values and those needed |
| # by sky130_fd_pr__res_generic_m3_check |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_m3_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 sky130_fd_pr__res_generic_m4 and sky130_fd_pr__res_generic_m5, depending on the |
| # back-end metal stack. |
| #---------------------------------------------------------------- |
| |
| proc sky130::sky130_fd_pr__res_generic_m4_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::sky130_fd_pr__res_generic_m5_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} |
| } |
| |
| #---------------------------------------------------------------- |
| # 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::sky130_fd_pr__res_iso_pw_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_po_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_high_po_0p35_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_high_po_0p69_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_high_po_1p41_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_high_po_2p85_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_high_po_5p73_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_xhigh_po_0p35_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_0p69_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_1p41_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_2p85_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_5p73_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_nd_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_pd_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_nd__hv_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_pd__hv_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_l1_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m1_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m2_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m3_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m4_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| proc sky130::sky130_fd_pr__res_generic_m5_convert {parameters} { |
| return [sky130::res_convert $parameters] |
| } |
| |
| #---------------------------------------------------------------- |
| # 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 |
| } |
| |
| if {[dict exists $parameters compatible]} { |
| set sellist [dict get $parameters compatible] |
| magic::add_selectlist gencell "Device type" $sellist $parameters $device |
| } |
| |
| # 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 |
| } |
| } |
| if {[dict exists $parameters guard]} { |
| magic::add_checkbox guard "Add guard ring" $parameters |
| |
| if {[dict exists $parameters hv_guard]} { |
| magic::add_checkbox hv_guard "High-voltage guard ring" $parameters |
| } |
| if {[dict exists $parameters n_guard]} { |
| magic::add_checkbox n_guard "N-well connected 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 |
| } |
| |
| |
| magic::add_entry viagb "Bottom guard ring via coverage \[+/-\](%)" $parameters |
| magic::add_entry viagt "Top guard ring via coverage \[+/-\](%)" $parameters |
| magic::add_entry viagr "Right guard ring via coverage \[+/-\](%)" $parameters |
| magic::add_entry viagl "Left guard ring via coverage \[+/-\](%)" $parameters |
| } |
| |
| if {[dict exists $parameters vias]} { |
| magic::add_checkbox vias "Add vias over contacts" $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::sky130_fd_pr__res_iso_pw_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_iso_pw $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_po_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_po $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_high_po_0p35_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_high_po_0p35 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_high_po_0p69_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_high_po_0p69 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_high_po_1p41_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_high_po_1p41 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_high_po_2p85_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_high_po_2p85 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_high_po_5p73_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_high_po_5p73 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_xhigh_po_0p35_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_xhigh_po_0p35 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_0p69_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_xhigh_po_0p69 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_1p41_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_xhigh_po_1p41 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_2p85_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_xhigh_po_2p85 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_xhigh_po_5p73_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_xhigh_po_5p73 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_nd_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_nd $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_pd_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_pd $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_nd__hv_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_nd__hv $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_pd__hv_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_pd__hv $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_l1_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_l1 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m1_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_m1 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m2_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_m2 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m3_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_m3 $parameters |
| } |
| |
| proc sky130::sky130_fd_pr__res_generic_m4_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_m4 $parameters |
| } |
| proc sky130::sky130_fd_pr__res_generic_m5_dialog {parameters} { |
| sky130::res_dialog sky130_fd_pr__res_generic_m5 $parameters |
| } |
| |
| #---------------------------------------------------------------- |
| # 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 vias 0 ;# add vias over contacts |
| set res_idtype none |
| |
| # 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} |
| if {"$res_idtype" != "none"} { |
| box grow c 2 |
| paint ${res_idtype} |
| } |
| 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} != ""} { |
| # Draw via over contact first |
| if {$vias != 0} { |
| pushbox |
| set ch $res_to_endcont |
| if {$ch < $via_size} {set ch $via_size} |
| set cw $epl |
| if {$cw < $via_size} {set cw $via_size} |
|