Implemented the photodiode device in the parameterized device
generator for magic.
diff --git a/sky130/magic/sky130.tcl b/sky130/magic/sky130.tcl
index bcf4df5..59bbc67 100644
--- a/sky130/magic/sky130.tcl
+++ b/sky130/magic/sky130.tcl
@@ -101,6 +101,8 @@
"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" \
@@ -868,6 +870,10 @@
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 \
@@ -927,6 +933,10 @@
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]
}
@@ -961,6 +971,10 @@
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
}
@@ -995,6 +1009,10 @@
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
}
@@ -1209,6 +1227,153 @@
}
#----------------------------------------------------------------
+# 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} {
@@ -1422,6 +1587,31 @@
}
#----------------------------------------------------------------
+# 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.
#----------------------------------------------------------------
diff --git a/sky130/magic/sky130.tech b/sky130/magic/sky130.tech
index b5546bf..7c5e949 100644
--- a/sky130/magic/sky130.tech
+++ b/sky130/magic/sky130.tech
@@ -82,6 +82,7 @@
# sky130_fd_pr__res_iso_pw rpw pwell resistor (in deep nwell)
# sky130_fd_pr__esd_nfet_g5v0d10v5 mvnfetesd ESD thickox nFET
# sky130_fd_pr__esd_pfet_g5v0d10v5 mvpfetesd ESD thickox pFET
+# sky130_fd_pr__photodiode photo Photodiode
#
# (*) Note that ppres may extract into some generic type called
# "sky130_fd_pr__res_xhigh_po", but only specific sizes of xhrpoly are
@@ -142,6 +143,7 @@
# Deep nwell
dwell dnwell,dnw
dwell isosubstrate,isosub
+ dwell photodiode,photo
# Wells
well nwell,nw
@@ -474,6 +476,7 @@
nwell nwell
pwell pwell
rpwell pwell ptransistor_stripes
+ photo nwell nwell_field_implant
ndiff ndiffusion
fomfill ndiffusion
pdiff pdiffusion
@@ -693,7 +696,7 @@
#-----------------------------------------------------
connect
- *nwell,*nsd,*mvnsd,dnwell,pnp *nwell,*nsd,*mvnsd,dnwell,pnp
+ *nwell,*nsd,*mvnsd,dnwell,pnp,photo *nwell,*nsd,*mvnsd,dnwell,pnp,photo
pwell,*psd,*mvpsd,npn pwell,*psd,*mvpsd,npn
*li,coreli,lifill *li,coreli,lifill
*m1,m1fill,obsmcon *m1,m1fill,obsmcon
@@ -751,7 +754,7 @@
# DNWELL
#----------------------------------------------------------------
- layer DNWELL dnwell,npn
+ layer DNWELL dnwell,npn,photo
calma 64 18
layer PWRES rpw
@@ -974,6 +977,9 @@
mask-hints PNPID
calma 82 44
+ layer PHOTO photo
+ calma 81 81
+
#----------------------------------------------------------------
# RPM
#----------------------------------------------------------------
@@ -2387,6 +2393,9 @@
and-not NWELL,nwelcheck
and NPNID
+ layer photo DNWELL
+ and PHOTO
+
layer rpw PWRES
and DNWELL
labels PWRES
@@ -3927,6 +3936,8 @@
calma CAPID 82 64
# Core area ID mark
calma COREID 81 2
+ # Photodiode ID mark
+ calma PHOTO 81 81
# Standard cell ID mark
calma STDCELL 81 4
# Padframe cell ID mark
@@ -4104,6 +4115,11 @@
"P+ diff cannot straddle Deep N-well (dnwell.5)"
variants (fast),(full)
+ width photo 3000 "Photodiode width < %d (photo.2)"
+ spacing photo photo 5000 touching_ok "Photodiode spacing < %d (photo.3)"
+ spacing photo dnwell 5300 touching_illegal \
+ "Photodiode spacing to deep nwell < %d (photo.4)"
+
#-----------------------------
# NWELL
#-----------------------------
@@ -5394,6 +5410,8 @@
device resistor sky130_fd_pr__res_generic_m4 rm4 *m4
device resistor sky130_fd_pr__res_generic_m5 rm5 *m5
#endif (METAL5)
+ device ndiode sky130_fd_pr__model__parasitic__diode_ps2dn \
+ photo pwell,space/w error a=area
device rsubcircuit sky130_fd_pr__res_high_po_0p35 xhrpoly \
xpc pwell,space/w error +res0p35 l=l