from math import floor
import gdsfactory as gf
from gdsfactory.typings import Float2, LayerSpec
[docs]
@gf.cell
def pmos(
diffusion_layer: LayerSpec = (65, 20),
poly_layer: LayerSpec = (66, 20),
gate_width: float = 0.42,
gate_length: float = 0.15,
sd_width: float = 0.3,
end_cap: float = 0.13,
contact_size: Float2 = (0.17, 0.17),
contact_spacing: Float2 = (0.17, 0.17),
contact_layer: LayerSpec = (66, 44),
contact_enclosure: Float2 = (0.06, 0.06),
diff_spacing: float = 0.27,
diff_enclosure: Float2 = (0.18, 0.18),
diffn_layer: LayerSpec = (65, 44),
nwell_layer: LayerSpec = (64, 20),
dnwell_enclosure: Float2 = (0.4, 0.4),
dnwell_layer: LayerSpec = (64, 18),
nf: int = 1,
sdm_enclosure: Float2 = (0.125, 0.125),
nsdm_layer: LayerSpec = (93, 44),
sdm_spacing: float = 0.13,
psdm_layer: LayerSpec = (94, 20),
li_width: float = 0.17,
li_layer: LayerSpec = (67, 20),
li_enclosure: float = 0.08,
mcon_layer: LayerSpec = (67, 44),
mcon_enclosure: Float2 = (0.03, 0.06),
m1_layer: LayerSpec = (68, 20),
npc_layer: LayerSpec = (95, 20),
npc_spacing: float = 0.09,
) -> gf.Component:
"""Return PMOS.
Args:
diffusion_layer: spec.
poly_layer: spec.
gate_width: for poly.
gate_length: for poly.
sd_width: source drain length.
end_cap : end cap length.
contact_size : contact dimension (length and width)
contact_layer : for contacts
contact_enclosure : for contacts within diffusion or poly
diff_spacing : for two adjacent diffusions
diff_enclosure : for diffusion within well
diffn_layer : for bulk tie
dnwell layer : for deep nwell
.. code::
_______
| poly |
_________| |_________ _
| sd_width| | sd_width| |
|<------->| |<------->| |gate_width
|_________| |_________| |
| Lg |
|<---->|
|______|
.. plot::
:include-source:
import sky130
c = sky130.pcells.pmos()
c.plot()
"""
c = gf.Component()
# generating poly and p+ diffusion
w_p = end_cap + gate_width + end_cap # poly total width
rect_p = gf.components.rectangle(size=(gate_length, w_p), layer=poly_layer)
# adding fingers
# poly = c.add_ref(rect_p)
poly = c.add_ref(rect_p, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
l_d = (nf + 1) * (sd_width + gate_length) - gate_length # n diffution total length
rect_d = gf.components.rectangle(size=(l_d, gate_width), layer=diffusion_layer)
diff_p = c.add_ref(rect_d)
poly.dmovex(sd_width)
poly.dmovey(-end_cap)
# generating p+ implant
rect_pm = gf.components.rectangle(
size=(l_d + 2 * sdm_enclosure[0], gate_width + 2 * sdm_enclosure[1]),
layer=psdm_layer,
)
psdm = c.add_ref(rect_pm)
psdm.dmovex(-sdm_enclosure[0])
psdm.dmovey(-sdm_enclosure[1])
# generating contacts and local interconnect and mcon and m1 of p+ diffusion
rect_c = gf.components.rectangle(size=contact_size, layer=contact_layer)
rect_mc = gf.components.rectangle(size=contact_size, layer=mcon_layer)
nr = floor(gate_width / (2 * contact_size[1]))
nc = floor(sd_width / (2 * contact_size[0]))
con_sp = list(contact_spacing)
con_sp[0] = con_sp[1] = contact_spacing[0] + contact_size[0]
min_gate_wid = 0.42
cont_arr1 = c.add_ref(rect_c, rows=nr, columns=nc, spacing=con_sp)
cont_arr2 = c.add_ref(rect_c, rows=nr, columns=nc, spacing=con_sp)
cont_arr1.dmovey((min_gate_wid - contact_size[1]) / 2)
cont_arr2.dmovey((min_gate_wid - contact_size[1]) / 2)
mcont_arr1 = c.add_ref(rect_mc, rows=nr, columns=nc, spacing=con_sp)
mcont_arr2 = c.add_ref(rect_mc, rows=nr, columns=nc, spacing=con_sp)
mcont_arr1.dmovey((min_gate_wid - contact_size[1]) / 2)
mcont_arr2.dmovey((min_gate_wid - contact_size[1]) / 2)
rect_lid = gf.components.rectangle(
size=(li_width, gate_width + li_enclosure), layer=li_layer
)
li1 = c.add_ref(rect_lid, rows=1, columns=nc, spacing=con_sp)
li2 = c.add_ref(rect_lid, rows=1, columns=nc, spacing=con_sp)
rect_m1d = gf.components.rectangle(
size=(contact_size[0] + 2 * mcon_enclosure[0], gate_width), layer=m1_layer
)
m1d1 = c.add_ref(rect_m1d, rows=1, columns=nc, spacing=con_sp)
m1d2 = c.add_ref(rect_m1d, rows=1, columns=nc, spacing=con_sp)
if nc > 1:
cont_arr1.dmovex((sd_width - (cont_arr1.dxmax - cont_arr1.dxmin)) / 2)
cont_arr2.dmovex(
(nf * (sd_width + gate_length))
+ ((sd_width - (cont_arr2.dxmax - cont_arr2.dxmin)) / 2)
)
mcont_arr1.dmovex((sd_width - (cont_arr1.dxmax - cont_arr1.dxmin)) / 2)
mcont_arr2.dmovex(
(nf * (sd_width + gate_length))
+ ((sd_width - (cont_arr2.dxmax - cont_arr2.dxmin)) / 2)
)
li1.dmovex((sd_width - (cont_arr1.dxmax - cont_arr1.dxmin)) / 2)
li2.dmovex(
(nf * (sd_width + gate_length))
+ ((sd_width - (cont_arr2.dxmax - cont_arr2.dxmin)) / 2)
)
m1d1.dmovex(
(sd_width - (cont_arr1.dxmax - cont_arr1.dxmin)) / 2 - mcon_enclosure[0]
)
m1d2.dmovex(
(nf * (sd_width + gate_length))
+ ((sd_width - (cont_arr2.dxmax - cont_arr2.dxmin)) / 2)
- mcon_enclosure[0]
)
else:
cont_arr1.dmovex((sd_width - contact_size[0]) / 2)
cont_arr2.dmovex(
(nf * (sd_width + gate_length)) + ((sd_width - contact_size[0]) / 2)
)
mcont_arr1.dmovex((sd_width - contact_size[0]) / 2)
mcont_arr2.dmovex(
(nf * (sd_width + gate_length)) + ((sd_width - contact_size[0]) / 2)
)
li1.dmovex((sd_width - contact_size[0]) / 2)
li2.dmovex((nf * (sd_width + gate_length)) + ((sd_width - contact_size[0]) / 2))
m1d1.dmovex((sd_width - contact_size[0]) / 2 - mcon_enclosure[0])
m1d2.dmovex(
(nf * (sd_width + gate_length))
+ ((sd_width - contact_size[0]) / 2)
- mcon_enclosure[0]
)
li1.dmovey(-li_enclosure / 2)
li2.dmovey(-li_enclosure / 2)
# generating contacts and local interconnects and mcon and m1 of poly
if gate_length <= contact_size[0]:
pc_x = contact_enclosure[0] + contact_size[0] + contact_enclosure[0]
cont_p = c.add_ref(
rect_c, rows=1, columns=nf, spacing=[sd_width + gate_length, 0]
)
cont_p.dmovex(sd_width - ((pc_x - gate_length) / 2) + contact_enclosure[0])
cont_p.dmovey(gate_width + end_cap + contact_enclosure[1])
cont_p2 = c.add_ref(
rect_c, rows=1, columns=nf, spacing=[sd_width + gate_length, 0]
)
cont_p2.dmovex(sd_width - ((pc_x - gate_length) / 2) + contact_enclosure[0])
cont_p2.dmovey(-end_cap - contact_enclosure[1] - contact_size[1])
mcont_p = c.add_ref(
rect_mc, rows=1, columns=nf, spacing=[sd_width + gate_length, 0]
)
mcont_p.dmovex(sd_width - ((pc_x - gate_length) / 2) + contact_enclosure[0])
mcont_p.dmovey(gate_width + end_cap + contact_enclosure[1])
mcont_p2 = c.add_ref(
rect_mc, rows=1, columns=nf, spacing=[sd_width + gate_length, 0]
)
mcont_p2.dmovex(sd_width - ((pc_x - gate_length) / 2) + contact_enclosure[0])
mcont_p2.dmovey(-end_cap - contact_enclosure[1] - contact_size[1])
else:
pc_x = gate_length
nc_p = floor(pc_x / (2 * contact_size[0]))
for i in range(nf):
cont_arr3 = c.add_ref(rect_c, rows=1, columns=nc_p, spacing=con_sp)
cont_arr3.dmovex(
sd_width
+ ((gate_length - (cont_arr3.dxmax - cont_arr3.dxmin)) / 2)
+ (i * (gate_length + sd_width))
)
cont_arr3.dmovey(gate_width + end_cap + contact_enclosure[1])
cont_arr5 = c.add_ref(rect_c, rows=1, columns=nc_p, spacing=con_sp)
cont_arr5.dmovex(
sd_width
+ ((gate_length - (cont_arr5.dxmax - cont_arr5.dxmin)) / 2)
+ (i * (gate_length + sd_width))
)
cont_arr5.dmovey(-contact_size[1] - end_cap - contact_enclosure[1])
mcont_arr3 = c.add_ref(rect_mc, rows=1, columns=nc_p, spacing=con_sp)
mcont_arr3.dmovex(
sd_width
+ ((gate_length - (cont_arr3.dxmax - cont_arr3.dxmin)) / 2)
+ (i * (gate_length + sd_width))
)
mcont_arr3.dmovey(gate_width + end_cap + contact_enclosure[1])
mcont_arr5 = c.add_ref(rect_mc, rows=1, columns=nc_p, spacing=con_sp)
mcont_arr5.dmovex(
sd_width
+ ((gate_length - (cont_arr5.dxmax - cont_arr5.dxmin)) / 2)
+ (i * (gate_length + sd_width))
)
mcont_arr5.dmovey(-contact_size[1] - end_cap - contact_enclosure[1])
pc_size = (
pc_x,
contact_enclosure[1] + contact_size[1] + contact_enclosure[1],
) # poly size to contain contact
rect_pc = gf.components.rectangle(size=pc_size, layer=poly_layer)
rect_m1p = gf.components.rectangle(
size=(
pc_x + 2 * mcon_enclosure[0] - 2 * contact_enclosure[0],
contact_size[1] + 2 * mcon_enclosure[1],
),
layer=m1_layer,
)
pc_u = c.add_ref(rect_pc, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
pc_u.dmovex(sd_width - ((pc_x - gate_length) / 2))
pc_u.dmovey(gate_width + end_cap)
pc_d = c.add_ref(rect_pc, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
pc_d.dmovex(sd_width - ((pc_x - gate_length) / 2))
pc_d.dmovey(-pc_size[1] - end_cap)
m1p_u = c.add_ref(rect_m1p, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
m1p_u.dmovex(
sd_width - ((pc_x - gate_length) / 2) + contact_enclosure[0] - mcon_enclosure[0]
)
m1p_u.dmovey(gate_width + end_cap + contact_enclosure[1] - mcon_enclosure[1])
m1p_d = c.add_ref(rect_m1p, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
m1p_d.dmovex(
sd_width - ((pc_x - gate_length) / 2) + contact_enclosure[0] - mcon_enclosure[0]
)
m1p_d.dmovey(-pc_size[1] - end_cap + contact_enclosure[1] - contact_enclosure[1])
rect_lip = gf.components.rectangle(
size=(pc_size[0] + li_enclosure, li_width), layer=li_layer
)
lip_u = c.add_ref(rect_lip, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
lip_u.dmovex(sd_width - ((pc_x - gate_length) / 2) - li_enclosure / 2)
lip_u.dmovey(gate_width + end_cap + contact_enclosure[1])
lip_d = c.add_ref(rect_lip, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
lip_d.dmovex(sd_width - ((pc_x - gate_length) / 2) - li_enclosure / 2)
lip_d.dmovey(-pc_size[1] - end_cap + contact_enclosure[1])
# generating npc for poly contacts
npc_en = end_cap - npc_spacing
rect_npc = gf.components.rectangle(
size=(pc_size[0] + npc_en, pc_size[1] + npc_en), layer=npc_layer
)
npc_u = c.add_ref(rect_npc, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
npc_u.dmovex(sd_width - ((pc_x - gate_length) / 2) - npc_en / 2)
npc_u.dmovey(gate_width + npc_spacing + npc_en / 2)
npc_d = c.add_ref(rect_npc, rows=1, columns=nf, spacing=[sd_width + gate_length, 0])
npc_d.dmovex(sd_width - ((pc_x - gate_length) / 2) - npc_en / 2)
npc_d.dmovey(-pc_size[1] - npc_en - npc_spacing - npc_en / 2)
# generaing n+ bulk tie and its contact and mcon and m1
rect_dn = gf.components.rectangle(size=(sd_width, gate_width), layer=diffn_layer)
diff_n = c.add_ref(rect_dn)
diff_n.connect(
"e1", diff_p.ports["e3"], allow_layer_mismatch=True, allow_width_mismatch=True
)
diff_n.dmovex(diff_spacing + sdm_spacing)
cont_arr4 = c.add_ref(rect_c, rows=nr, columns=nc, spacing=con_sp)
cont_arr4.dmovey((min_gate_wid - contact_size[1]) / 2)
mcont_arr4 = c.add_ref(rect_mc, rows=nr, columns=nc, spacing=con_sp)
mcont_arr4.dmovey((min_gate_wid - contact_size[1]) / 2)
rect_m1dn = gf.components.rectangle(
size=(contact_size[0] + 2 * mcon_enclosure[0], gate_width), layer=m1_layer
)
m1dn = c.add_ref(rect_m1dn, rows=1, columns=nc, spacing=con_sp)
# generate its local interconnects
li4 = c.add_ref(rect_lid, rows=1, columns=nc, spacing=con_sp)
if nc > 1:
cont_arr4.dmovex(
l_d
+ diff_spacing
+ sdm_spacing
+ ((sd_width - (cont_arr4.dxmax - cont_arr4.dxmin)) / 2)
)
mcont_arr4.dmovex(
l_d
+ diff_spacing
+ sdm_spacing
+ ((sd_width - (cont_arr4.dxmax - cont_arr4.dxmin)) / 2)
)
li4.dmovex(
l_d
+ diff_spacing
+ sdm_spacing
+ ((sd_width - (cont_arr4.dxmax - cont_arr4.dxmin)) / 2)
)
m1dn.dmovex(
l_d
+ diff_spacing
+ sdm_spacing
+ ((sd_width - (cont_arr4.dxmax - cont_arr4.dxmin)) / 2)
- mcon_enclosure[0]
)
else:
cont_arr4.dmovex(
l_d + diff_spacing + sdm_spacing + ((sd_width - contact_size[0]) / 2)
)
mcont_arr4.dmovex(
l_d + diff_spacing + sdm_spacing + ((sd_width - contact_size[0]) / 2)
)
li4.dmovex(
l_d + diff_spacing + sdm_spacing + ((sd_width - contact_size[0]) / 2)
)
m1dn.dmovex(
l_d
+ diff_spacing
+ sdm_spacing
+ ((sd_width - contact_size[0]) / 2)
- mcon_enclosure[0]
)
li4.dmovey(-li_enclosure / 2)
# generating n+ implant for bulk tie
rect_nm = gf.components.rectangle(
size=(sd_width + 2 * sdm_enclosure[0], gate_width + 2 * sdm_enclosure[1]),
layer=nsdm_layer,
)
nsdm = c.add_ref(rect_nm)
nsdm.connect(
"e1", diff_p.ports["e3"], allow_layer_mismatch=True, allow_width_mismatch=True
)
nsdm.dmovex(diff_spacing + sdm_spacing - sdm_enclosure[0])
# generating nwell
rect_nw = gf.components.rectangle(
size=(
2 * diff_enclosure[0] + l_d + diff_spacing + sdm_spacing + sd_width,
2 * diff_enclosure[1] + gate_width,
),
layer=nwell_layer,
)
nwell = c.add_ref(rect_nw)
nwell.dmovex(-diff_enclosure[0])
nwell.dmovey(-diff_enclosure[1])
# generating deep nwell
rect_dnw = gf.components.rectangle(
size=(
rect_nw.dxmax - rect_nw.dxmin + 2 * dnwell_enclosure[0],
rect_nw.dymax - rect_nw.dymin + 2 * dnwell_enclosure[1],
),
layer=dnwell_layer,
)
dnwell = c.add_ref(rect_dnw)
dnwell.dmovex(-diff_enclosure[0] - dnwell_enclosure[0])
dnwell.dmovey(-diff_enclosure[1] - dnwell_enclosure[1])
return c
if __name__ == "__main__":
# c = pmos(gate_length= 2, gate_width=10, sd_width=5)
c = pmos()
c.show()