Source code for ihp.cells.transistors

"""Transistor components for IHP PDK."""

import gdsfactory as gf
from gdsfactory import Component
from gdsfactory.typings import LayerSpec


[docs] @gf.cell def nmos( width: float = 1.0, length: float = 0.13, nf: int = 1, m: int = 1, model: str = "sg13_lv_nmos", layer_gatpoly: LayerSpec = "GatPolydrawing", layer_activ: LayerSpec = "Activdrawing", layer_nsd: LayerSpec = "nSDdrawing", layer_cont: LayerSpec = "Contdrawing", layer_metal1: LayerSpec = "Metal1drawing", ) -> Component: """Create an NMOS transistor. Args: width: Total width of the transistor in micrometers. length: Gate length in micrometers. nf: Number of fingers. m: Multiplier (number of parallel devices). model: Device model name. layer_gatpoly: Gate polysilicon layer. layer_activ: Active region layer. layer_nsd: N+ source/drain implant layer. layer_cont: Contact layer. layer_metal1: Metal1 layer. Returns: Component with NMOS transistor layout. """ c = Component() # Design rules gate_min_width = 0.15 gate_min_length = 0.13 cont_size = 0.16 cont_spacing = 0.18 cont_gate_spacing = 0.14 cont_enc_active = 0.07 cont_enc_metal = 0.06 poly_extension = 0.18 active_extension = 0.23 psd_enclosure = 0.12 # Calculate dimensions gate_width = max(width / nf, gate_min_width) gate_length = max(length, gate_min_length) # Grid snap grid = 0.005 gate_width = round(gate_width / grid) * grid gate_length = round(gate_length / grid) * grid # Create transistor fingers finger_pitch = gate_width + 2 * cont_gate_spacing + cont_size for i in range(nf): x_offset = i * finger_pitch # Gate poly gate = gf.components.rectangle( size=(gate_length, gate_width + 2 * poly_extension), layer=layer_gatpoly, ) gate_ref = c.add_ref(gate) gate_ref.movex(x_offset) # Active region active_width = gate_width active_length = gate_length + 2 * active_extension active = gf.components.rectangle( size=(active_length, active_width), layer=layer_activ, ) active_ref = c.add_ref(active) active_ref.move((x_offset - active_extension, poly_extension)) # Source/Drain contacts # Calculate number of contacts n_cont_y = int((active_width - cont_size) / cont_spacing) + 1 # Source contacts (left) for j in range(n_cont_y): y_pos = poly_extension + j * cont_spacing cont = gf.components.rectangle( size=(cont_size, cont_size), layer=layer_cont, ) cont_ref = c.add_ref(cont) cont_ref.move((x_offset - active_extension + cont_enc_active, y_pos)) # Metal1 for source m1 = gf.components.rectangle( size=(cont_size + 2 * cont_enc_metal, cont_size + 2 * cont_enc_metal), layer=layer_metal1, ) m1_ref = c.add_ref(m1) m1_ref.move( ( x_offset - active_extension + cont_enc_active - cont_enc_metal, y_pos - cont_enc_metal, ) ) # Drain contacts (right) for j in range(n_cont_y): y_pos = poly_extension + j * cont_spacing cont = gf.components.rectangle( size=(cont_size, cont_size), layer=layer_cont, ) cont_ref = c.add_ref(cont) cont_ref.move((x_offset + gate_length + cont_gate_spacing, y_pos)) # Metal1 for drain m1 = gf.components.rectangle( size=(cont_size + 2 * cont_enc_metal, cont_size + 2 * cont_enc_metal), layer=layer_metal1, ) m1_ref = c.add_ref(m1) m1_ref.move( ( x_offset + gate_length + cont_gate_spacing - cont_enc_metal, y_pos - cont_enc_metal, ) ) # N+ implant nsd = gf.components.rectangle( size=(nf * finger_pitch + active_extension, gate_width + 2 * psd_enclosure), layer=layer_nsd, ) nsd_ref = c.add_ref(nsd) nsd_ref.move((-active_extension - psd_enclosure, poly_extension - psd_enclosure)) # Add ports c.add_port( name="G", center=(nf * finger_pitch / 2, -poly_extension), width=gate_length, orientation=270, layer=layer_gatpoly, port_type="electrical", ) c.add_port( name="S", center=(-active_extension, gate_width / 2 + poly_extension), width=gate_width, orientation=180, layer=layer_metal1, port_type="electrical", ) c.add_port( name="D", center=(gate_length + active_extension, gate_width / 2 + poly_extension), width=gate_width, orientation=0, layer=layer_metal1, port_type="electrical", ) # Add metadata c.info["model"] = model c.info["width"] = width c.info["length"] = length c.info["nf"] = nf c.info["m"] = m c.info["type"] = "nmos" return c
[docs] @gf.cell def pmos( width: float = 1.0, length: float = 0.13, nf: int = 1, m: int = 1, model: str = "sg13_lv_pmos", layer_nwell: LayerSpec = "NWelldrawing", layer_gatpoly: LayerSpec = "GatPolydrawing", layer_activ: LayerSpec = "Activdrawing", layer_psd: LayerSpec = "pSDdrawing", layer_cont: LayerSpec = "Contdrawing", layer_metal1: LayerSpec = "Metal1drawing", ) -> Component: """Create a PMOS transistor. Args: width: Total width of the transistor in micrometers. length: Gate length in micrometers. nf: Number of fingers. m: Multiplier (number of parallel devices). model: Device model name. layer_nwell: N-well layer. layer_gatpoly: Gate polysilicon layer. layer_activ: Active region layer. layer_psd: P+ source/drain implant layer. layer_cont: Contact layer. layer_metal1: Metal1 layer. Returns: Component with PMOS transistor layout. """ c = Component() # Design rules gate_min_width = 0.15 gate_min_length = 0.13 cont_size = 0.16 cont_spacing = 0.18 cont_gate_spacing = 0.14 cont_enc_active = 0.07 cont_enc_metal = 0.06 poly_extension = 0.18 active_extension = 0.23 nwell_enclosure = 0.31 psd_enclosure = 0.12 # Calculate dimensions gate_width = max(width / nf, gate_min_width) gate_length = max(length, gate_min_length) # Grid snap grid = 0.005 gate_width = round(gate_width / grid) * grid gate_length = round(gate_length / grid) * grid # N-Well nwell_width = gate_width + 2 * nwell_enclosure nwell_length = gate_length + 2 * active_extension + 2 * nwell_enclosure nwell = gf.components.rectangle( size=(nwell_length * nf, nwell_width), layer=layer_nwell, ) nwell_ref = c.add_ref(nwell) nwell_ref.move( (-active_extension - nwell_enclosure, poly_extension - nwell_enclosure) ) # Create transistor fingers finger_pitch = gate_width + 2 * cont_gate_spacing + cont_size for i in range(nf): x_offset = i * finger_pitch # Gate poly gate = gf.components.rectangle( size=(gate_length, gate_width + 2 * poly_extension), layer=layer_gatpoly, ) gate_ref = c.add_ref(gate) gate_ref.movex(x_offset) # Active region active_width = gate_width active_length = gate_length + 2 * active_extension active = gf.components.rectangle( size=(active_length, active_width), layer=layer_activ, ) active_ref = c.add_ref(active) active_ref.move((x_offset - active_extension, poly_extension)) # Source/Drain contacts n_cont_y = int((active_width - cont_size) / cont_spacing) + 1 # Source contacts (left) for j in range(n_cont_y): y_pos = poly_extension + j * cont_spacing cont = gf.components.rectangle( size=(cont_size, cont_size), layer=layer_cont, ) cont_ref = c.add_ref(cont) cont_ref.move((x_offset - active_extension + cont_enc_active, y_pos)) # Metal1 for source m1 = gf.components.rectangle( size=(cont_size + 2 * cont_enc_metal, cont_size + 2 * cont_enc_metal), layer=layer_metal1, ) m1_ref = c.add_ref(m1) m1_ref.move( ( x_offset - active_extension + cont_enc_active - cont_enc_metal, y_pos - cont_enc_metal, ) ) # Drain contacts (right) for j in range(n_cont_y): y_pos = poly_extension + j * cont_spacing cont = gf.components.rectangle( size=(cont_size, cont_size), layer=layer_cont, ) cont_ref = c.add_ref(cont) cont_ref.move((x_offset + gate_length + cont_gate_spacing, y_pos)) # Metal1 for drain m1 = gf.components.rectangle( size=(cont_size + 2 * cont_enc_metal, cont_size + 2 * cont_enc_metal), layer=layer_metal1, ) m1_ref = c.add_ref(m1) m1_ref.move( ( x_offset + gate_length + cont_gate_spacing - cont_enc_metal, y_pos - cont_enc_metal, ) ) # P+ implant psd = gf.components.rectangle( size=(nf * finger_pitch + active_extension, gate_width + 2 * psd_enclosure), layer=layer_psd, ) psd_ref = c.add_ref(psd) psd_ref.move((-active_extension - psd_enclosure, poly_extension - psd_enclosure)) # Add ports c.add_port( name="G", center=(nf * finger_pitch / 2, -poly_extension), width=gate_length, orientation=270, layer=layer_gatpoly, port_type="electrical", ) c.add_port( name="S", center=(-active_extension, gate_width / 2 + poly_extension), width=gate_width, orientation=180, layer=layer_metal1, port_type="electrical", ) c.add_port( name="D", center=(gate_length + active_extension, gate_width / 2 + poly_extension), width=gate_width, orientation=0, layer=layer_metal1, port_type="electrical", ) # Add metadata c.info["model"] = model c.info["width"] = width c.info["length"] = length c.info["nf"] = nf c.info["m"] = m c.info["type"] = "pmos" return c
[docs] @gf.cell def nmos_hv( width: float = 1.0, length: float = 0.45, nf: int = 1, m: int = 1, model: str = "sg13_hv_nmos", layer_thickgateox: LayerSpec = "ThickGateOxdrawing", layer_gatpoly: LayerSpec = "GatPolydrawing", layer_activ: LayerSpec = "Activdrawing", layer_nsd: LayerSpec = "nSDdrawing", layer_cont: LayerSpec = "Contdrawing", layer_metal1: LayerSpec = "Metal1drawing", ) -> Component: """Create a high-voltage NMOS transistor. Args: width: Total width of the transistor in micrometers. length: Gate length in micrometers. nf: Number of fingers. m: Multiplier (number of parallel devices). model: Device model name. layer_thickgateox: Thick gate oxide layer. layer_gatpoly: Gate polysilicon layer. layer_activ: Active region layer. layer_nsd: N+ source/drain implant layer. layer_cont: Contact layer. layer_metal1: Metal1 layer. Returns: Component with HV NMOS transistor layout. """ c = Component() # Add base nmos as reference nmos_ref = c << nmos( width=width, length=length, nf=nf, m=m, model=model, layer_gatpoly=layer_gatpoly, layer_activ=layer_activ, layer_nsd=layer_nsd, layer_cont=layer_cont, layer_metal1=layer_metal1, ) # Add thick gate oxide layer thick_ox = gf.components.rectangle( size=(length + 0.5, width + 0.5), layer=layer_thickgateox, centered=True, ) c.add_ref(thick_ox) # Copy ports from base nmos c.add_ports(nmos_ref.ports) c.info["type"] = "nmos_hv" return c
[docs] @gf.cell def pmos_hv( width: float = 1.0, length: float = 0.45, nf: int = 1, m: int = 1, model: str = "sg13_hv_pmos", layer_thickgateox: LayerSpec = "ThickGateOxdrawing", layer_nwell: LayerSpec = "NWelldrawing", layer_gatpoly: LayerSpec = "GatPolydrawing", layer_activ: LayerSpec = "Activdrawing", layer_psd: LayerSpec = "pSDdrawing", layer_cont: LayerSpec = "Contdrawing", layer_metal1: LayerSpec = "Metal1drawing", ) -> Component: """Create a high-voltage PMOS transistor. Args: width: Total width of the transistor in micrometers. length: Gate length in micrometers. nf: Number of fingers. m: Multiplier (number of parallel devices). model: Device model name. layer_thickgateox: Thick gate oxide layer. layer_nwell: N-well layer. layer_gatpoly: Gate polysilicon layer. layer_activ: Active region layer. layer_psd: P+ source/drain implant layer. layer_cont: Contact layer. layer_metal1: Metal1 layer. Returns: Component with HV PMOS transistor layout. """ c = Component() # Add base pmos as reference pmos_ref = c << pmos( width=width, length=length, nf=nf, m=m, model=model, layer_nwell=layer_nwell, layer_gatpoly=layer_gatpoly, layer_activ=layer_activ, layer_psd=layer_psd, layer_cont=layer_cont, layer_metal1=layer_metal1, ) # Add thick gate oxide layer thick_ox = gf.components.rectangle( size=(length + 0.5, width + 0.5), layer=layer_thickgateox, centered=True, ) c.add_ref(thick_ox) # Copy ports from base pmos c.add_ports(pmos_ref.ports) c.info["type"] = "pmos_hv" return c
[docs] @gf.cell def rfnmos( width: float = 2.0, length: float = 0.13, nf: int = 2, m: int = 1, model: str = "sg13_lv_rfnmos", layer_gatpoly: LayerSpec = "GatPolydrawing", layer_activ: LayerSpec = "Activdrawing", layer_nsd: LayerSpec = "nSDdrawing", layer_cont: LayerSpec = "Contdrawing", layer_metal1: LayerSpec = "Metal1drawing", ) -> Component: """Create an RF NMOS transistor with optimized layout. Args: width: Total width of the transistor in micrometers. length: Gate length in micrometers. nf: Number of fingers (should be even for RF). m: Multiplier (number of parallel devices). model: Device model name. layer_gatpoly: Gate polysilicon layer. layer_activ: Active region layer. layer_nsd: N+ source/drain implant layer. layer_cont: Contact layer. layer_metal1: Metal1 layer. Returns: Component with RF NMOS transistor layout. """ # Ensure even number of fingers for RF layout if nf % 2 != 0: nf = nf + 1 c = Component() # Add base nmos as reference nmos_ref = c << nmos( width=width, length=length, nf=nf, m=m, model=model, layer_gatpoly=layer_gatpoly, layer_activ=layer_activ, layer_nsd=layer_nsd, layer_cont=layer_cont, layer_metal1=layer_metal1, ) # Add substrate shielding for RF shield_layer = (37, 0) # Example shield layer shield = gf.components.rectangle( size=(length * nf * 1.5, width * 1.2), layer=shield_layer, centered=True, ) c.add_ref(shield) # Copy ports from base nmos c.add_ports(nmos_ref.ports) c.info["type"] = "rfnmos" return c
[docs] @gf.cell def rfpmos( width: float = 2.0, length: float = 0.13, nf: int = 2, m: int = 1, model: str = "sg13_lv_rfpmos", layer_nwell: LayerSpec = "NWelldrawing", layer_gatpoly: LayerSpec = "GatPolydrawing", layer_activ: LayerSpec = "Activdrawing", layer_psd: LayerSpec = "pSDdrawing", layer_cont: LayerSpec = "Contdrawing", layer_metal1: LayerSpec = "Metal1drawing", ) -> Component: """Create an RF PMOS transistor with optimized layout. Args: width: Total width of the transistor in micrometers. length: Gate length in micrometers. nf: Number of fingers (should be even for RF). m: Multiplier (number of parallel devices). model: Device model name. layer_nwell: N-well layer. layer_gatpoly: Gate polysilicon layer. layer_activ: Active region layer. layer_psd: P+ source/drain implant layer. layer_cont: Contact layer. layer_metal1: Metal1 layer. Returns: Component with RF PMOS transistor layout. """ # Ensure even number of fingers for RF layout if nf % 2 != 0: nf = nf + 1 c = Component() # Add base pmos as reference pmos_ref = c << pmos( width=width, length=length, nf=nf, m=m, model=model, layer_nwell=layer_nwell, layer_gatpoly=layer_gatpoly, layer_activ=layer_activ, layer_psd=layer_psd, layer_cont=layer_cont, layer_metal1=layer_metal1, ) # Add substrate shielding for RF shield_layer = (37, 0) # Example shield layer shield = gf.components.rectangle( size=(length * nf * 1.5, width * 1.2), layer=shield_layer, centered=True, ) c.add_ref(shield) # Copy ports from base pmos c.add_ports(pmos_ref.ports) c.info["type"] = "rfpmos" return c
if __name__ == "__main__": from gdsfactory.difftest import xor from ihp import PDK from ihp.cells import fixed PDK.activate() # Test the components c0 = fixed.nmos() # original c1 = nmos() # New # c = gf.grid([c0, c1], spacing=100) c = xor(c0, c1) c.show() # c0 = cells.pmos() # original # c1 = pmos() # New # # c = gf.grid([c0, c1], spacing=100) # c = xor(c0, c1) # c.show() # c0 = cells.rfnmos() # original # c1 = rfnmos() # New # # c = gf.grid([c0, c1], spacing=100) # c = xor(c0, c1) # c.show()