Source code for ihp.cells.bjt_transistors

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

import math

import gdsfactory as gf
from gdsfactory.typings import LayerSpec

from ihp.tech import TECH as _TECH


def fix(value):
    if type(value) is float:
        return int(math.floor(value))
    else:
        return value


def _snap_width_to_grid(width_um: float) -> float:
    """Snap port width to the nearest multiple of 0.002 um (2 DBU = 0.002 um).

    Args:
        width_um: Port width in microns.

    Returns:
        Width snapped to the nearest valid grid multiple.
    """
    grid = 0.002
    w = max(width_um, grid)
    return round(w / grid) * grid


[docs] @gf.cell def npn13G2( baspolyx: float = 0.3, bipwinx: float = 0.07, bipwiny: float = 0.1, empolyx: float = 0.15, empolyy: float = 0.18, STI: float = 0.44, emitter_length: float = 0.9, emitter_width: float = 0.7, Nx: int = 1, Ny: int = 1, text: str = "npn13G2", CMetY1: float = 0, CMetY2: float = 0, ) -> gf.Component: """Returns the IHP npn13G2 BJT transistor as a gdsfactory Component. Args: Nx: Number of emitter fingers in the x-direction. Ny: Number of emitter fingers in the y-direction. emitter_length: Length of the emitter region in microns. emitter_width: Width of the emitter region in microns. STI: Shallow Trench Isolation width in microns. baspolyx: Base poly extension in x-direction in microns. bipwinx: Bipolar window extension in x-direction in microns. bipwiny: Bipolar window extension in y-direction in microns. empolyx: Emitter poly extension in x-direction in microns. empolyy: Emitter poly extension in y-direction in microns. text: Text label for the transistor. CMetY1: Contact metal Y1 dimension in microns. CMetY2: Contact metal Y2 dimension in microns. Returns: gdsfactory.Component: The generated npn13G2 transistor layout. Raises: ValueError: If finger count is outside allowed range. """ total_nx = Nx * Ny if total_nx < _TECH.npn_min_nx or total_nx > _TECH.npn_max_nx: raise ValueError( f"npn13G2 Nx*Ny={total_nx} out of range [{_TECH.npn_min_nx}, {_TECH.npn_max_nx}]" ) c = gf.Component() layer_via1: LayerSpec = "Via1drawing" layer_metal1: LayerSpec = "Metal1drawing" layer_cont: LayerSpec = "Contdrawing" layer_emwind: LayerSpec = "EmWinddrawing" layer_activmask: LayerSpec = "Activmask" layer_activ: LayerSpec = "Activdrawing" layer_metal1: LayerSpec = "Metal1drawing" layer_metal1_pin: LayerSpec = "Metal1pin" layer_metal2_pin: LayerSpec = "Metal2pin" layer_metal2: LayerSpec = "Metal2drawing" layer_nSDblock: LayerSpec = "nSDblock" layer_text: LayerSpec = "TEXTdrawing" layer_trans: LayerSpec = "TRANSdrawing" layer_pSD: LayerSpec = "pSDdrawing" ActivShift = 0.01 ActivShift = 0.0 # for multiplied npn: le has to be bigger stepX = 1.85 stretchX = stepX * (Nx - 1) # stretchX = stepX * (Nx - 1) bipwinyoffset = (2 * (bipwiny - 0.1) - 0) / 2 empolyyoffset = (2 * (empolyy - 0.18)) / 2 empolyxoffset = (2 * (empolyx - 0.15)) / 2 baspolyxoffset = (2 * (baspolyx - 0.3)) / 2 STIoffset = (2 * (STI - 0.44)) / 2 tmp = emitter_length le = emitter_width we = tmp nSDBlockShift = ( 0.43 - le ) # 23.07.09: needed to draw nSDBlock shorter in small pCell leoffset = 0 # ((le - 0.07) / 2) ############## # npn13G2_base pcStepY = 0.41 yOffset = 0.20 pcRepeatY = 4 if Nx > 1: CMetY1 = 1.01 + we / 2 + leoffset + bipwinyoffset + empolyyoffset CMetY2 = 0.57 + we / 2 + leoffset + bipwinyoffset + empolyyoffset else: CMetY1 = 0.8 + we / 2 + leoffset + bipwinyoffset + empolyyoffset CMetY2 = 0.56 + we / 2 + leoffset + bipwinyoffset + empolyyoffset for pcIndexX in range(int(math.floor(Nx))): # loop for generate the given number of vias in variable pcRepeatY # two vias are generated per loop for pcIndexY in range(int(math.floor(pcRepeatY))): # Via on left side via1_size = 0.19 left = (stepX * pcIndexX) - 0.3 bottom = ( -( (-0.3 - yOffset - leoffset - bipwinyoffset - empolyyoffset) + (pcIndexY * pcStepY) ) + 0.2 - via1_size ) c.add_ref( gf.components.rectangle( size=( via1_size, via1_size, ), layer=layer_via1, ) ).move((left, bottom)) left = (stepX * pcIndexX) + 0.11 # Via on the right side c.add_ref( gf.components.rectangle( size=( via1_size, via1_size, ), layer=layer_via1, ) ).move((left, bottom)) # Emitter metal left = (stepX * pcIndexX) - 0.35 bottom = -(0.335 + we / 2 + leoffset + bipwinyoffset + empolyyoffset) right = stepX * pcIndexX + 0.35 top = -(-0.32 - we / 2 - leoffset - bipwinyoffset - empolyyoffset) c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal1, ) ).move((left, bottom)) # Cont layer left = stepX * pcIndexX - 0.79 - le / 2 top = -(-0.76 - we / 2 - leoffset - bipwinyoffset - empolyyoffset) right = stepX * pcIndexX + 0.79 + le / 2 bottom = -(-0.6 - we / 2 - leoffset - bipwinyoffset - empolyyoffset) c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_cont, ) ).move((left, bottom)) left = stepX * pcIndexX - 0.76 top = -(0.61 + we / 2 - leoffset - bipwinyoffset - empolyyoffset) right = stepX * pcIndexX + 0.76 bottom = -(0.77 + we / 2 - leoffset - bipwinyoffset - empolyyoffset) c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_cont, ) ).move((left, bottom)) # EmWind left = stepX * pcIndexX - le / 2 top = we / 2 + leoffset right = stepX * pcIndexX + le / 2 bottom = -we / 2 - leoffset c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_emwind, ) ).move((left, bottom)) # Activmask xl = stepX * pcIndexX - 0.06 xh = xl + 0.12 yl = -0.24 - leoffset yh = -yl c.add_polygon( [ (xh + 0.865, -yl + 0.74), (xl - 0.865, -yl + 0.74), (xl - 0.865, -yh - 0.38), (xl - 0.385, -yh - 0.38), (xl - 0.175, -yh - 0.59), (xh + 0.175, -yh - 0.59), (xh + 0.385, -yh - 0.38), (xh + 0.865, -yh - 0.38), ], layer=layer_activmask, ) # Activ left = ( stepX * pcIndexX - 0.89 - le / 2 - empolyxoffset - baspolyxoffset - STIoffset ) top = -(-0.83 - we / 2 - leoffset - bipwinyoffset - empolyyoffset) right = ( stepX * pcIndexX + 0.89 + le / 2 + empolyxoffset + baspolyxoffset + STIoffset ) bottom = -(-0.89 - we / 2 + 0.36 - leoffset - bipwinyoffset - empolyyoffset) c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_activ, ) ).move((left, bottom)) c.add_polygon( [ ( stepX * pcIndexX + 0.94 + le / 2 + empolyxoffset + baspolyxoffset + STIoffset, -(1.98 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stepX * pcIndexX + 0.94 + le / 2 + empolyxoffset + baspolyxoffset + STIoffset, -(0.45 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stepX * pcIndexX + 0.52 + le / 2 + empolyxoffset + baspolyxoffset + STIoffset, -(0.03 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stepX * pcIndexX + 0.52 + le / 2 + empolyxoffset + baspolyxoffset + STIoffset, -( -0.6 - we / 2 + leoffset + bipwinyoffset + empolyyoffset + nSDBlockShift ), ), ( stepX * pcIndexX + 0.27 + le / 2 + empolyxoffset + baspolyxoffset + STIoffset, -( -0.85 - we / 2 + leoffset + bipwinyoffset + empolyyoffset + nSDBlockShift ), ), ( stepX * pcIndexX - 0.27 - le / 2 - empolyxoffset - baspolyxoffset - STIoffset, -( -0.85 - we / 2 + leoffset + bipwinyoffset + empolyyoffset + nSDBlockShift ), ), ( stepX * pcIndexX - 0.52 - le / 2 - empolyxoffset - baspolyxoffset - STIoffset, -( -0.6 - we / 2 + leoffset + bipwinyoffset + empolyyoffset + nSDBlockShift ), ), ( stepX * pcIndexX - 0.52 - le / 2 - empolyxoffset - baspolyxoffset - STIoffset, -(0.03 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stepX * pcIndexX - 0.94 - le / 2 - empolyxoffset - baspolyxoffset - STIoffset, -(0.45 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stepX * pcIndexX - 0.94 - le / 2 - empolyxoffset - baspolyxoffset - STIoffset, -(1.98 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ], layer=layer_nSDblock, ) # Collector metal left = -0.89 - le / 2 top = CMetY1 right = stretchX + 0.89 + le / 2 bottom = CMetY2 c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal1, ) ).move((left, bottom)) # Base metal left = -0.94 - le / 2 bottom = -(0.81 + we / 2 + leoffset + bipwinyoffset + empolyyoffset) right = stretchX + 0.94 + le / 2 top = -(0.57 + we / 2 + leoffset + bipwinyoffset + empolyyoffset) c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal1, ) ).move((left, bottom)) # Metal2 left = -0.89 - le / 2 bottom = -(0.335 + we / 2 + leoffset + bipwinyoffset + empolyyoffset) right = stretchX + 0.89 + le / 2 top = -(-0.32 - we / 2 - leoffset - bipwinyoffset - empolyyoffset) c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal2, ) ).move((left, bottom)) c.add_label( text=text, layer=layer_text, position=( 0.015, 1.86 + we / 2 + leoffset + bipwinyoffset + empolyyoffset, ), ) c.add_polygon( [ ( stretchX + 2.45, (2.43 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), (-2.45, (2.43 + we / 2 + leoffset + bipwinyoffset + empolyyoffset)), (-2.45, (-1.98 - we / 2 - leoffset - bipwinyoffset - empolyyoffset)), ( stretchX + 2.45, (-1.98 - we / 2 - leoffset - bipwinyoffset - empolyyoffset), ), ], layer=layer_trans, ) c.add_polygon( [ ( stretchX + 3.35, (3.33 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stretchX + 2.45, (3.33 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stretchX + 2.45, (-1.98 - we / 2 - leoffset - bipwinyoffset - empolyyoffset), ), (-2.45, (-1.98 - we / 2 - leoffset - bipwinyoffset - empolyyoffset)), (-2.45, (2.43 + we / 2 + leoffset + bipwinyoffset + empolyyoffset)), ( stretchX + 2.45, (2.43 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), ( stretchX + 2.45, (3.33 + we / 2 + leoffset + bipwinyoffset + empolyyoffset), ), (-3.35, (3.33 + we / 2 + leoffset + bipwinyoffset + empolyyoffset)), (-3.35, (-2.88 - we / 2 - leoffset - bipwinyoffset - empolyyoffset)), ( stretchX + 3.35, (-2.88 - we / 2 - leoffset - bipwinyoffset - empolyyoffset), ), ], layer=layer_pSD, ) c.add_polygon( [ ( stretchX + 3.15 + ActivShift, 3.13 + we / 2 + leoffset + bipwinyoffset + empolyyoffset + ActivShift, ), ( stretchX + 2.65 + ActivShift, 3.13 + we / 2 + leoffset + bipwinyoffset + empolyyoffset + ActivShift, ), ( stretchX + 2.65 + ActivShift, -2.18 - we / 2 - leoffset - bipwinyoffset - empolyyoffset - ActivShift, ), ( -2.65 - ActivShift, -2.18 - we / 2 - leoffset - bipwinyoffset - empolyyoffset - ActivShift, ), ( -2.65 - ActivShift, 2.63 + we / 2 + leoffset + bipwinyoffset + empolyyoffset + ActivShift, ), ( stretchX + 2.65 + ActivShift, 2.63 + we / 2 + leoffset + bipwinyoffset + empolyyoffset + ActivShift, ), ( stretchX + 2.65 + ActivShift, 3.13 + we / 2 + leoffset + bipwinyoffset + empolyyoffset + ActivShift, ), ( -3.15 - ActivShift, 3.13 + we / 2 + leoffset + bipwinyoffset + empolyyoffset + ActivShift, ), ( -3.15 - ActivShift, -2.68 - we / 2 - leoffset - bipwinyoffset - empolyyoffset - ActivShift, ), ( stretchX + 3.15 + ActivShift, -2.68 - we / 2 - leoffset - bipwinyoffset - empolyyoffset - ActivShift, ), ], layer=layer_activ, ) if Nx > 1: left = -0.89 - le / 2 bottom = 0.57 + we / 2 - leoffset - bipwinyoffset - empolyyoffset right = stretchX + 0.89 + le / 2 top = 1.01 + we / 2 - leoffset - bipwinyoffset - empolyyoffset c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal1_pin, ) ).move((left, bottom)) c.add_label( text="C", layer=layer_text, position=( 0.5 * (left + right), 0.5 * (top + bottom), ), ) else: left = -0.89 - le / 2 bottom = 0.56 + we / 2 + leoffset + bipwinyoffset + empolyyoffset right = stretchX + 0.89 + le / 2 top = 0.8 + we / 2 + leoffset + bipwinyoffset + empolyyoffset c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal1_pin, ) ).move((left, bottom)) c.add_label( text="C", layer=layer_text, position=( 0.5 * (left + right), 0.5 * (top + bottom), ), ) # Collector port c.add_port( "C", center=(0.5 * (left + right), 0.5 * (top + bottom)), width=_snap_width_to_grid(top - bottom), layer=layer_metal1_pin, orientation=180.0, port_type="electrical", ) left = -0.94 - le / 2 bottom = -0.81 - we / 2 - leoffset - bipwinyoffset - empolyyoffset right = stretchX + 0.94 + le / 2 top = -0.57 - we / 2 - leoffset - bipwinyoffset - empolyyoffset c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal1_pin, ) ).move((left, bottom)) c.add_label( text="B", layer=layer_text, position=( 0.5 * (left + right), 0.5 * (top + bottom), ), ) # Base port c.add_port( "B", center=(0.5 * (left + right), 0.5 * (top + bottom)), width=_snap_width_to_grid(top - bottom), layer=layer_metal1_pin, orientation=180.0, port_type="electrical", ) left = -0.71 - le / 2 bottom = -0.335 - we / 2 - leoffset - bipwinyoffset - empolyyoffset right = stretchX + 0.71 + le / 2 top = 0.32 + we / 2 + leoffset + bipwinyoffset + empolyyoffset c.add_ref( gf.components.rectangle( size=( right - left, top - bottom, ), layer=layer_metal2_pin, ) ).move((left, bottom)) c.add_label( text="E", layer=layer_text, position=( 0.5 * (left + right), 0.5 * (top + bottom), ), ) pcLabelText = f"Ae={int(Nx):d}*{int(Ny):d}*{le:.2f}*{we:.2f}" c.add_label(text=pcLabelText, layer=layer_text, position=(-1.977, -2.546)) # Emitter port c.add_port( "E", center=(0.5 * (left + right), 0.5 * (top + bottom)), width=_snap_width_to_grid(top - bottom), layer=layer_metal2_pin, orientation=180.0, port_type="electrical", ) # VLSIR Simulation Metadata c.info["vlsir"] = { "model": "npn13G2", "spice_type": "SUBCKT", "spice_lib": "sg13g2_hbt_mod.lib", "port_order": ["c", "b", "e", "bn"], "port_map": {"C": "c", "B": "b", "E": "e"}, "params": { "Nx": Nx, "Ny": Ny, "we": emitter_width * 1e-6, "le": emitter_length * 1e-6, }, } # TODO: Extend to handle empoly, bipwin, cmet return c
[docs] @gf.cell def npn13G2L( emitter_length: float = 1, emitter_width: float = 0.07, Nx: int = 1, ) -> gf.Component: """Builds the IHP npn13G2L BJT transistor as a gdsfactory Component. The transistor geometry is defined by the number of emitter fingers and the dimensions of each emitter finger. Args: emitter_length: Length of each emitter finger, in microns. emitter_width: Width of each emitter finger, in microns. Nx: Number of emitter fingers. Returns: gdsfactory.Component: The generated npn13G2L transistor layout. Raises: ValueError: If finger count is outside allowed range. """ if Nx < _TECH.npn_min_nx or Nx > _TECH.npn_max_nx: raise ValueError( f"npn13G2L Nx={Nx} out of range [{_TECH.npn_min_nx}, {_TECH.npn_max_nx}]" ) c = gf.Component() layer_EmWind: LayerSpec = "EmWinddrawing" layer_HeatTrans: LayerSpec = "HeatTransdrawing" layer_activ: LayerSpec = "Activdrawing" layer_activ_mask: LayerSpec = "Activmask" layer_via1: LayerSpec = "Via1drawing" layer_cont: LayerSpec = "Contdrawing" layer_metal1: LayerSpec = "Metal1drawing" layer_metal1_pin: LayerSpec = "Metal1pin" layer_metal2: LayerSpec = "Metal2drawing" layer_metal2_pin: LayerSpec = "Metal2pin" layer_trans: LayerSpec = "TRANSdrawing" layer_text: LayerSpec = "TEXTdrawing" layer_pSD: LayerSpec = "pSDdrawing" le = emitter_length we = emitter_width # masterLib = "SG13_dev" # emPoly_enc_vert = 0.16 # emPoly_enc_hori = 0.13 emWindOrigin_x = 3.865 emWindOrigin_y = 3.1 # BiWind_enc_vert = 0.1 # BiWind_enc_hori = 0.07 # ColWind_enc_vert = 0.58 # ColWind_enc_hori = 1.515 Activ_enc_vert = 0.28 Activ_enc_hori = 1.365 # BasPoly_enc_vert = 0.45 # BasPoly_enc_hori = 0.58 Col_Metal1_distance = 0.975 Col_Metal1_width = 0.39 Bas_Metal1_distance = 0.32 Bas_Metal1_width = 0.16 Emi_Metal1_enc_vert = 0.2 Emi_Metal1_enc_hori = 0.095 column_pitch = 2.8 c.add_ref( gf.components.rectangle( size=(we, le), layer=layer_EmWind, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x, emWindOrigin_y)) c.add_ref( gf.components.rectangle( size=( we + 0.1, le + 0.1, ), layer=layer_HeatTrans, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - 0.05, emWindOrigin_y - 0.05)) c.add_label( text="npn13G2L", layer=layer_HeatTrans, position=( 0.5 * (2 * emWindOrigin_x + we), 0.5 * (2 * emWindOrigin_y + le), ), ) # Activ Drawing outer = c << gf.components.rectangle( size=( we + 2 * Activ_enc_hori, le + 2 * Activ_enc_vert, ), layer=layer_activ, ) outer.move((emWindOrigin_x - Activ_enc_hori, emWindOrigin_y - Activ_enc_vert)) # Activ mask inner = c << gf.components.rectangle( size=( 0.705 - Emi_Metal1_enc_hori, le + 2 * Activ_enc_vert, ), layer=layer_activ_mask, ) inner.move((emWindOrigin_x - 0.705, emWindOrigin_y - Activ_enc_vert)) inner1 = c << gf.components.rectangle( size=( 0.705 - Emi_Metal1_enc_hori, le + 2 * Activ_enc_vert, ), layer=layer_activ_mask, ) inner1.move( (emWindOrigin_x + we + Emi_Metal1_enc_hori, emWindOrigin_y - Activ_enc_vert) ) # Combine mask's rectangles in order to remove them from activ inners = gf.boolean(inner, inner1, operation="or", layer=layer_activ_mask) c.add_ref(inners, columns=Nx, column_pitch=column_pitch) c.add_ref( gf.boolean( outer, inners, operation="not", layer=layer_activ, layer1=layer_activ, layer2=layer_activ_mask, ), columns=Nx, column_pitch=column_pitch, ) # Delete the rectangle that was covering the whole region outer.delete() # Draw contacts and Via c.add_ref( gf.components.rectangle( size=( 0.19, 0.2 + le, ), layer=layer_via1, ), columns=Nx, column_pitch=column_pitch, ).move((3.805, 3)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.3 + le, ), layer=layer_cont, ), columns=Nx, column_pitch=column_pitch, ).move((2.68, 2.95)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.3 + le, ), layer=layer_cont, ), columns=Nx, column_pitch=column_pitch, ).move((3.82, 2.95)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.3 + le, ), layer=layer_cont, ), columns=Nx, column_pitch=column_pitch, ).move((4.96, 2.95)) cont_cnt = fix((le + 0.21) / (0.16 + 0.18)) for i in range(int(cont_cnt + 1)): c.add_ref( gf.components.rectangle( size=( 0.16, 0.16, ), layer=layer_cont, ), columns=Nx, column_pitch=column_pitch, ).move((3.385, 2.89 + i * (0.16 + 0.18))) c.add_ref( gf.components.rectangle( size=( 0.16, 0.16, ), layer=layer_cont, ), columns=Nx, column_pitch=column_pitch, ).move((4.255, 2.89 + i * (0.16 + 0.18))) # Metals # Metal Path upwards # Collector c.add_ref( gf.components.rectangle( size=( Col_Metal1_width, 4.1 - 2.82 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 2.82)) c.add_ref( gf.components.rectangle( size=( Col_Metal1_width, 4.1 - 2.82 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x + we + Col_Metal1_distance, 2.82)) c.add_ref( gf.components.rectangle( size=( 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width, 0.65, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 4.1 + le)) collector_pin_xmin = emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width collector_pin_xmax = ( collector_pin_xmin + 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width ) # The maximum x depends on the number of elements collector_pin_xmax += (Nx - 1) * (collector_pin_xmax - collector_pin_xmin) collector_pin_ymin = 4.1 + le collector_pin_ymax = collector_pin_ymin + 0.65 c.add_ref( gf.components.rectangle( size=( 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width, 0.65, ), layer=layer_metal1_pin, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 4.1 + le)) c.add_ref( gf.components.rectangle( size=( Bas_Metal1_width, 1.28 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width, 2.1)) c.add_ref( gf.components.rectangle( size=( Bas_Metal1_width, 1.28 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x + we + Bas_Metal1_distance, 2.1)) c.add_ref( gf.components.rectangle( size=( 2 * Bas_Metal1_distance + we + 2 * Bas_Metal1_width, 0.65, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width, 1.45)) base_pin_xmin = emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width base_pin_xmax = base_pin_xmin + 2 * Bas_Metal1_distance + we + 2 * Bas_Metal1_width # The maximum x depends on the number of elements base_pin_xmax += (Nx - 1) * column_pitch base_pin_ymin = 1.45 base_pin_ymax = base_pin_ymin + 0.65 c.add_ref( gf.components.rectangle( size=( 2 * Bas_Metal1_distance + we + 2 * Bas_Metal1_width, 0.65, ), layer=layer_metal1_pin, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width, 1.45)) # Emitter c.add_ref( gf.components.rectangle( size=( we + 2 * Emi_Metal1_enc_hori, le + 2 * Emi_Metal1_enc_vert, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Emi_Metal1_enc_hori, emWindOrigin_y - Emi_Metal1_enc_vert)) c.add_ref( gf.components.rectangle( size=( we + 2 * Col_Metal1_distance + 2 * Col_Metal1_width, le + 0.4, ), layer=layer_metal2, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 2.9)) emitter_pin_xmin = emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width emitter_pin_xmax = ( emitter_pin_xmin + 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width ) # The maximum x depends on the number of elements emitter_pin_xmax += (Nx - 1) * (emitter_pin_xmax - emitter_pin_xmin) emitter_pin_ymin = 2.9 emitter_pin_ymax = emitter_pin_ymin + le + 0.4 c.add_ref( gf.components.rectangle( size=( we + 2 * Col_Metal1_distance + 2 * Col_Metal1_width, le + 0.4, ), layer=layer_metal2_pin, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 2.9)) # Draw Guard Ring c.add_ref( gf.components.rectangle( size=( 6 + ((Nx - 1) * 2.8), le + 4.4, ), layer=layer_trans, ) ).move((0.9, 0.9)) outer = c << gf.components.rectangle( size=( 7.8 + ((Nx - 1) * 2.8), le + 6.2, ), layer=layer_pSD, ) inner = c << gf.components.rectangle( size=( 6 + ((Nx - 1) * 2.8), le + 4.4, ), layer=layer_pSD, ) inner.move((0.9, 0.9)) c.add_ref( gf.boolean( outer, inner, operation="not", layer=layer_pSD, layer1=layer_pSD, layer2=layer_pSD, ) ) # Delete the rectangle that was covering the whole region outer.delete() # Delete the inner rectangle used for boolean inner.delete() outer = c << gf.components.rectangle( size=( 7.4 + ((Nx - 1) * 2.8), le + 5.8, ), layer=layer_activ, ) outer.move((0.2, 0.2)) inner = c << gf.components.rectangle( size=( 6.4 + ((Nx - 1) * 2.8), le + 4.8, ), layer=layer_activ, ) inner.move((0.7, 0.7)) c.add_ref( gf.boolean( outer, inner, operation="not", layer=layer_activ, layer1=layer_activ, layer2=layer_activ, ) ) # Delete the rectangle that was covering the whole region outer.delete() # Delete the inner rectangle used for boolean inner.delete() # Texts pcLabelText = f"Ae={int(Nx):d}*{1:d}*{le:.2f}*{we:.2f}" c.add_label(text=pcLabelText, layer=layer_text, position=(1.5, 1.0)) c.add_label(text="npn13G2L", layer=layer_text, position=(1.75, 1.0)) if Nx > 1: c.add_ref( gf.components.rectangle( size=( 1.77, 0.65, ), layer=layer_metal1, ), columns=Nx - 1, column_pitch=column_pitch, ).move((4.415, 1.45)) c.add_ref( gf.components.rectangle( size=( 1.77, 0.65, ), layer=layer_metal1_pin, ), columns=Nx - 1, column_pitch=column_pitch, ).move((4.415, 1.45)) # Ports # Collector port c.add_port( "C", center=( 0.5 * (collector_pin_xmin + collector_pin_xmax), 0.5 * (collector_pin_ymin + collector_pin_ymax), ), width=_snap_width_to_grid(collector_pin_ymax - collector_pin_ymin), layer=layer_metal1_pin, orientation=180.0, port_type="electrical", ) c.add_label( text="C", layer=layer_text, position=( 0.5 * (collector_pin_xmin + collector_pin_xmax), 0.5 * (collector_pin_ymin + collector_pin_ymax), ), ) # Base port c.add_port( "B", center=( 0.5 * (base_pin_xmin + base_pin_xmax), 0.5 * (base_pin_ymin + base_pin_ymax), ), width=_snap_width_to_grid(base_pin_ymax - base_pin_ymin), layer=layer_metal1_pin, orientation=180.0, port_type="electrical", ) c.add_label( text="B", layer=layer_text, position=( 0.5 * (base_pin_xmin + base_pin_xmax), 0.5 * (base_pin_ymin + base_pin_ymax), ), ) # Emitter port c.add_port( "E", center=( 0.5 * (emitter_pin_xmin + emitter_pin_xmax), 0.5 * (emitter_pin_ymin + emitter_pin_ymax), ), width=_snap_width_to_grid(emitter_pin_ymax - emitter_pin_ymin), layer=layer_metal2_pin, orientation=180.0, port_type="electrical", ) c.add_label( text="E", layer=layer_text, position=( 0.5 * (emitter_pin_xmin + emitter_pin_xmax), 0.5 * (emitter_pin_ymin + emitter_pin_ymax), ), ) # VLSIR Simulation Metadata c.info["vlsir"] = { "model": "npn13G2l", "spice_type": "SUBCKT", "spice_lib": "sg13g2_hbt_mod.lib", "port_order": ["c", "b", "e", "bn"], "port_map": {"C": "c", "B": "b", "E": "e"}, "params": { "we": emitter_width * 1e-6, "le": emitter_length * 1e-6, }, } return c
[docs] @gf.cell def npn13G2V( emitter_length: float = 1, emitter_width: float = 0.12, Nx: int = 1, ) -> gf.Component: """Builds the IHP npn13G2V BJT transistor as a gdsfactory Component. The transistor geometry is defined by the number of emitter fingers and the dimensions of each emitter finger. Args: emitter_length: Length of each emitter finger, in microns. emitter_width: Width of each emitter finger, in microns. Nx: Number of emitter fingers. Returns: gdsfactory.Component: The generated npn13G2V transistor layout. Raises: ValueError: If finger count is outside allowed range. """ if Nx < _TECH.npn_min_nx or Nx > _TECH.npn_max_nx: raise ValueError( f"npn13G2V Nx={Nx} out of range [{_TECH.npn_min_nx}, {_TECH.npn_max_nx}]" ) c = gf.Component() layer_EmWiHV: LayerSpec = "EmWiHVdrawing" layer_HeatTrans: LayerSpec = "HeatTransdrawing" layer_activ: LayerSpec = "Activdrawing" layer_activ_mask: LayerSpec = "Activmask" layer_via1: LayerSpec = "Via1drawing" layer_cont: LayerSpec = "Contdrawing" layer_metal1: LayerSpec = "Metal1drawing" layer_metal1_pin: LayerSpec = "Metal1pin" layer_metal2: LayerSpec = "Metal2drawing" layer_metal2_pin: LayerSpec = "Metal2pin" layer_trans: LayerSpec = "TRANSdrawing" layer_text: LayerSpec = "TEXTdrawing" layer_pSD: LayerSpec = "pSDdrawing" le = emitter_length we = emitter_width # masterLib = "SG13_dev" emWindOrigin_x = 3.81 emWindOrigin_y = 3.1 Activ_enc_vert = 0.28 Activ_enc_hori = 1.11 Col_Metal1_distance = 0.79 Col_Metal1_width = 0.32 Bas_Metal1_distance = 0.295 Bas_Metal1_width = 0.17 Emi_Metal1_enc_vert = 0.28 Emi_Metal1_enc_hori = 0.07 Via1Width = _TECH.via1_size_rf Via1Space = _TECH.via1_spacing_narrow m1EncVia1 = _TECH.via1_enc column_pitch = 2.34 c.add_ref( gf.components.rectangle( size=(we, le), layer=layer_EmWiHV, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x, emWindOrigin_y)) c.add_ref( gf.components.rectangle( size=( we + 0.1, le + 0.1, ), layer=layer_HeatTrans, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - 0.05, emWindOrigin_y - 0.05)) c.add_label( text="npn13G2V", layer=layer_HeatTrans, position=( 0.5 * (2 * emWindOrigin_x + we), 0.5 * (2 * emWindOrigin_y + le), ), ) # Activ Drawing outer = c << gf.components.rectangle( size=( we + 2 * Activ_enc_hori, le + 2 * Activ_enc_vert, ), layer=layer_activ, ) outer.move((emWindOrigin_x - Activ_enc_hori, emWindOrigin_y - Activ_enc_vert)) # Activ mask inner = c << gf.components.rectangle( size=( 0.705 - Emi_Metal1_enc_hori, le + 2 * Activ_enc_vert, ), layer=layer_activ_mask, ) inner.move((emWindOrigin_x - 0.705, emWindOrigin_y - Activ_enc_vert)) inner1 = c << gf.components.rectangle( size=( 0.705 - Emi_Metal1_enc_hori, le + 2 * Activ_enc_vert, ), layer=layer_activ_mask, ) inner1.move( (emWindOrigin_x + we + Emi_Metal1_enc_hori, emWindOrigin_y - Activ_enc_vert) ) # Combine mask's rectangles in order to remove them from activ inners = gf.boolean(inner, inner1, operation="xor", layer=layer_activ_mask) c.add_ref(inners, columns=Nx, column_pitch=column_pitch) c.add_ref( gf.boolean( outer, inners, operation="not", layer=layer_activ, layer1=layer_activ, layer2=layer_activ_mask, ), columns=Nx, column_pitch=column_pitch, ) # Delete the rectangle that was covering the whole region outer.delete() # Draw Via via_cnt = int((le + 0.46) / (0.19 + 0.22)) emMet1_height = le + 2 * Emi_Metal1_enc_vert viaColumn = ( via_cnt * Via1Width + (via_cnt - 1) * Via1Space + (Via1Width + Via1Space) + 0.05 + m1EncVia1 ) if emMet1_height < viaColumn: via_cnt -= 1 c.add_ref( gf.components.rectangle( size=( 0.19, 0.19, ), layer=layer_via1, ), rows=via_cnt + 1, row_pitch=0.41, columns=Nx, column_pitch=column_pitch, ).move((3.775, 2.87)) # Draw contacts cont_cnt = int(fix((le + 0.21) / (0.16 + 0.18))) c.add_ref( gf.components.rectangle( size=( 0.16, 0.12 + le, ), layer=layer_cont, ), columns=Nx, column_pitch=column_pitch, ).move((3.79, 3.04)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.16, ), layer=layer_cont, ), rows=cont_cnt + 1, row_pitch=0.34, columns=Nx, column_pitch=column_pitch, ).move((2.8, 2.89)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.16, ), layer=layer_cont, ), rows=cont_cnt + 1, row_pitch=0.34, columns=Nx, column_pitch=column_pitch, ).move((3.35, 2.89)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.16, ), layer=layer_cont, ), rows=cont_cnt + 1, row_pitch=0.34, columns=Nx, column_pitch=column_pitch, ).move((4.23, 2.89)) c.add_ref( gf.components.rectangle( size=( 0.16, 0.16, ), layer=layer_cont, ), rows=cont_cnt + 1, row_pitch=0.34, columns=Nx, column_pitch=column_pitch, ).move((4.78, 2.89)) # Metals # Metal Path upwards # Collector c.add_ref( gf.components.rectangle( size=( Col_Metal1_width, 4.1 - 2.82 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 2.82)) c.add_ref( gf.components.rectangle( size=( Col_Metal1_width, 4.1 - 2.82 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x + we + Col_Metal1_distance, 2.82)) c.add_ref( gf.components.rectangle( size=( 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width, 0.65, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 4.1 + le)) collector_pin_xmin = emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width collector_pin_xmax = ( collector_pin_xmin + 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width ) # The maximum x depends on the number of elements collector_pin_xmax += (Nx - 1) * (collector_pin_xmax - collector_pin_xmin) collector_pin_ymin = 4.1 + le collector_pin_ymax = collector_pin_ymin + 0.65 c.add_ref( gf.components.rectangle( size=( 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width, 0.65, ), layer=layer_metal1_pin, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 4.1 + le)) c.add_ref( gf.components.rectangle( size=( Bas_Metal1_width, 1.28 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width, 2.1)) c.add_ref( gf.components.rectangle( size=( Bas_Metal1_width, 1.28 + le, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x + we + Bas_Metal1_distance, 2.1)) c.add_ref( gf.components.rectangle( size=( 2 * Bas_Metal1_distance + we + 2 * Bas_Metal1_width, 0.65, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width, 1.45)) base_pin_xmin = emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width base_pin_xmax = base_pin_xmin + 2 * Bas_Metal1_distance + we + 2 * Bas_Metal1_width # The maximum x depends on the number of elements base_pin_xmax += (Nx - 1) * column_pitch base_pin_ymin = 1.45 base_pin_ymax = base_pin_ymin + 0.65 c.add_ref( gf.components.rectangle( size=( 2 * Bas_Metal1_distance + we + 2 * Bas_Metal1_width, 0.65, ), layer=layer_metal1_pin, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Bas_Metal1_distance - Bas_Metal1_width, 1.45)) # Emitter c.add_ref( gf.components.rectangle( size=( we + 2 * Emi_Metal1_enc_hori, le + 2 * Emi_Metal1_enc_vert, ), layer=layer_metal1, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Emi_Metal1_enc_hori, emWindOrigin_y - Emi_Metal1_enc_vert)) c.add_ref( gf.components.rectangle( size=( we + 2 * Col_Metal1_distance + 2 * Col_Metal1_width, le + 0.56, ), layer=layer_metal2, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 2.82)) emitter_pin_xmin = emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width emitter_pin_xmax = ( emitter_pin_xmin + 2 * Col_Metal1_distance + we + 2 * Col_Metal1_width ) # The maximum x depends on the number of elements emitter_pin_xmax += (Nx - 1) * (emitter_pin_xmax - emitter_pin_xmin) emitter_pin_ymin = 2.9 emitter_pin_ymax = emitter_pin_ymin + le + 0.4 c.add_ref( gf.components.rectangle( size=( we + 2 * Col_Metal1_distance + 2 * Col_Metal1_width, le + 0.56, ), layer=layer_metal2_pin, ), columns=Nx, column_pitch=column_pitch, ).move((emWindOrigin_x - Col_Metal1_distance - Col_Metal1_width, 2.82)) # Draw Guard Ring c.add_ref( gf.components.rectangle( size=( 5.94 + ((Nx - 1) * 2.34), le + 4.4, ), layer=layer_trans, ) ).move((0.9, 0.9)) outer = c << gf.components.rectangle( size=( 7.74 + ((Nx - 1) * 2.34), le + 6.2, ), layer=layer_pSD, ) inner = c << gf.components.rectangle( size=( 5.94 + ((Nx - 1) * 2.34), le + 4.4, ), layer=layer_pSD, ) inner.move((0.9, 0.9)) c.add_ref( gf.boolean( outer, inner, operation="not", layer=layer_pSD, layer1=layer_pSD, layer2=layer_pSD, ) ) # Delete the rectangle that was covering the whole region outer.delete() # Delete the inner rectangle used for boolean inner.delete() outer = c << gf.components.rectangle( size=( 7.34 + ((Nx - 1) * 2.34), le + 5.8, ), layer=layer_activ, ) outer.move((0.2, 0.2)) inner = c << gf.components.rectangle( size=( 6.34 + ((Nx - 1) * 2.34), le + 4.8, ), layer=layer_activ, ) inner.move((0.7, 0.7)) c.add_ref( gf.boolean( outer, inner, operation="not", layer=layer_activ, layer1=layer_activ, layer2=layer_activ, ) ) # Delete the rectangle that was covering the whole region outer.delete() # Delete the inner rectangle used for boolean inner.delete() # Texts pcLabelText = f"Ae={int(Nx):d}*{1:d}*{le:.2f}*{we:.2f}" c.add_label(text=pcLabelText, layer=layer_text, position=(1.5, 1.0)) c.add_label(text="npn13G2L", layer=layer_text, position=(1.75, 1.0)) if Nx > 1: c.add_ref( gf.components.rectangle( size=( 1.3, 0.65, ), layer=layer_metal1, ), columns=Nx - 1, column_pitch=column_pitch, ).move((4.395, 1.45)) c.add_ref( gf.components.rectangle( size=( 1.3, 0.65, ), layer=layer_metal1_pin, ), columns=Nx - 1, column_pitch=column_pitch, ).move((4.395, 1.45)) # Ports # Collector port c.add_port( "C", center=( 0.5 * (collector_pin_xmin + collector_pin_xmax), 0.5 * (collector_pin_ymin + collector_pin_ymax), ), width=_snap_width_to_grid(collector_pin_ymax - collector_pin_ymin), layer=layer_metal1_pin, orientation=180.0, port_type="electrical", ) c.add_label( text="C", layer=layer_text, position=( 0.5 * (collector_pin_xmin + collector_pin_xmax), 0.5 * (collector_pin_ymin + collector_pin_ymax), ), ) # Base port c.add_port( "B", center=( 0.5 * (base_pin_xmin + base_pin_xmax), 0.5 * (base_pin_ymin + base_pin_ymax), ), width=_snap_width_to_grid(base_pin_ymax - base_pin_ymin), layer=layer_metal1_pin, orientation=180.0, port_type="electrical", ) c.add_label( text="B", layer=layer_text, position=( 0.5 * (base_pin_xmin + base_pin_xmax), 0.5 * (base_pin_ymin + base_pin_ymax), ), ) # Emitter port c.add_port( "E", center=( 0.5 * (emitter_pin_xmin + emitter_pin_xmax), 0.5 * (emitter_pin_ymin + emitter_pin_ymax), ), width=_snap_width_to_grid(emitter_pin_ymax - emitter_pin_ymin), layer=layer_metal2_pin, orientation=180.0, port_type="electrical", ) c.add_label( text="E", layer=layer_text, position=( 0.5 * (emitter_pin_xmin + emitter_pin_xmax), 0.5 * (emitter_pin_ymin + emitter_pin_ymax), ), ) # VLSIR Simulation Metadata c.info["vlsir"] = { "model": "npn13G2v", "spice_type": "SUBCKT", "spice_lib": "sg13g2_hbt_mod.lib", "port_order": ["c", "b", "e", "bn"], "port_map": {"C": "c", "B": "b", "E": "e"}, "params": { "we": emitter_width * 1e-6, "le": emitter_length * 1e-6, }, } return c
def tog(x: float) -> float: SG13_GRID = 0.005 SG13_EPSILON = 0.001 SG13_IGRID = 1.0 / SG13_GRID return fix(x * SG13_IGRID + SG13_EPSILON) * SG13_GRID def contactArray( c: gf.Component, length: float, width: float, contactLayer: LayerSpec, xl: float, yl: float, ox: float, oy: float, ws: float, ds: float, ) -> None: """ Distributes as many square contact of size ws, into a rectangle of (length, width), with distances >= ds. The distances are adjusted so that the outer contacts have fixed distances ox and oy from the sides of the rectangle. Args: c : gf.Component The GDSFactory component on which the array is placed. length : float Length (x-dimension) of the region which contains the pin array. width : float Width (y-dimension) of the region which contains the pin array. xl: float Minimum x-coordinate of the array that contains the pins. yl: float Minimum y-coordinate of the array that contains the pins. ox: float Distance from edge in x direction. oy: float Distance from edge in y direction. ws : float Dimension, x and y, of the individual square contact. ds: float Distance between first column from left edge, last column from right edge, first (bottom) row and bottom edge, and last (top) row and top edge. """ eps = _TECH.epsilon nx = math.floor((length - ox * 2 + ds) / (ws + ds) + eps) dsx = 0 if nx == 1: dsx = 0 else: dsx = (length - ox * 2 - ws * nx) / (nx - 1) ny = math.floor((width - oy * 2 + ds) / (ws + ds) + eps) dsy = 0 if ny == 1: dsy = 0 else: dsy = (width - oy * 2 - ws * ny) / (ny - 1) x = 0 if nx == 1: x = (length - ws) / 2 else: x = ox for _ in range(int(nx)): # for(i=1; i<=nx; i++) { y = 0 if ny == 1: y = (width - ws) / 2 else: y = oy for _ in range(int(ny)): # for(j=1; j<=ny; j++) { contact_ref = c << gf.components.rectangle( size=(ws, ws), layer=contactLayer, ) contact_ref.move((tog(x) + xl, tog(y) + yl)) y = y + ws + dsy x = x + ws + dsx
[docs] @gf.cell def pnpMPA(length: float = 2, width: float = 0.7) -> gf.Component: """Returns the IHP pnpMPA BJT transistor as a gdsfactory Component. This function generates a layout for a PNP transistor using the IHP process. The geometry of the transistor is defined by its width and length. Args: length: Length of the transistor, in microns. width: Width of the transistor, in microns. Returns: gdsfactory.Component: The generated pnpMPA transistor layout. Raises: ValueError: If width or length is outside allowed range. """ if width < _TECH.pnp_min_width or width > _TECH.pnp_max_width: raise ValueError( f"pnpMPA width={width} out of range [{_TECH.pnp_min_width}, {_TECH.pnp_max_width}]" ) if length < _TECH.pnp_min_length or length > _TECH.pnp_max_length: raise ValueError( f"pnpMPA length={length} out of range [{_TECH.pnp_min_length}, {_TECH.pnp_max_length}]" ) c = gf.Component() SG13_GRID = _TECH.grid SG13_IGRID = 1.0 / SG13_GRID epsilon = _TECH.epsilon hact = (fix(length * SG13_IGRID + epsilon) * SG13_GRID) * 0.5 wact = (fix(width * SG13_IGRID + epsilon) * SG13_GRID) * 0.5 Cnt_a = _TECH.cont_size Cnt_b = _TECH.cont_spacing Cnt_b1 = _TECH.cont_b1 M1_c1 = _TECH.m1_endcap pSD_c = _TECH.psd_activ_over w1m1 = wact - 0.02 h1m1 = hact - 0.02 wpsd = wact + 0.21 hpsd = hact + 0.18 w2act = wpsd + pSD_c h2act = hpsd + pSD_c dw2act = max(wact, 0.3) dh2act = 0.29 w2m1 = w2act + 0.02 h2m1 = h2act + 0.02 dw2m1 = dw2act - 0.04 dh2m1 = dh2act - 0.04 wbulay = w2act + dw2act + 0.05 hbulay = h2act + dh2act + 0.05 wnwell = wbulay + 0.26 hnwell = hbulay + 0.26 w2psd = wnwell + 0.5 h2psd = hnwell + 0.5 d2psd = 0.75 w3act = w2psd + 0.2 h3act = h2psd + 0.2 d3act = 0.35 activLayer: LayerSpec = "Activdrawing" # 1 contLayer: LayerSpec = "Contdrawing" # 6 metal1Layer: LayerSpec = "Metal1drawing" # 8 metal1_pin_Layer: LayerSpec = "Metal1pin" # 8 pSdLayer: LayerSpec = "pSDdrawing" # 14 nwellLayer: LayerSpec = "NWelldrawing" # 31 nBuLayer: LayerSpec = "nBuLaydrawing" # 32 textLayer: LayerSpec = "TEXTdrawing" # 63 c.add_ref( gf.components.rectangle(size=(2 * wact, 2 * hact), layer=activLayer) ).move((-wact, -hact)) # Labels c.add_label( text="PLUS", layer=textLayer, ) c.add_label( text="MINUS", layer=textLayer, position=(-w2m1 - dw2m1 / 2, 0), ) c.add_label(text="pnpMPA", layer=textLayer, position=(0, -(hnwell + h2psd) / 2)) c.add_ref(gf.components.rectangle(size=(2 * wpsd, 2 * hpsd), layer=pSdLayer)).move( (-wpsd, -hpsd) ) _xl = -w1m1 _xh = w1m1 _yl = -h1m1 _yh = h1m1 _ox = M1_c1 _oy = M1_c1 _ws = Cnt_a _ds = Cnt_b vg4 = (Cnt_a + Cnt_b) * 4 + Cnt_a + _ox * 2 if _xh - _xl >= vg4 and _yh - _yl >= vg4: _ds = Cnt_b1 contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) ref1 = c << gf.components.rectangle(size=(2 * w2act, 2 * h2act), layer=activLayer) ref1.move((-w2act, -h2act)) ref2 = c << gf.components.rectangle( size=(2 * w2act + 2 * dw2act, 2 * h2act + 2 * dh2act), layer=activLayer ) ref2.move((-w2act - dw2act, -h2act - dh2act)) c.add_ref( gf.boolean( ref2, ref1, operation="xor", layer=activLayer, layer1=activLayer, layer2=activLayer, ) ) # Delete the rectangle that was covering the whole region ref1.delete() # Delete the inner rectangle used for boolean ref2.delete() # Metals ref1 = c << gf.components.rectangle(size=(2 * w2m1, 2 * h2m1), layer=metal1Layer) ref1.move((-w2m1, -h2m1)) ref2 = c << gf.components.rectangle( size=(2 * w2m1 + 2 * dw2m1, 2 * h2m1 + 2 * dh2m1), layer=metal1Layer ) ref2.move((-w2m1 - dw2m1, -h2m1 - dh2m1)) c.add_ref( gf.boolean( ref2, ref1, operation="xor", layer=metal1Layer, layer1=metal1Layer, layer2=metal1Layer, ) ) # Delete the rectangle that was covering the whole region ref1.delete() # Delete the inner rectangle used for boolean ref2.delete() _xl = -w2m1 - dw2m1 _xh = -w2m1 _yl = -h2m1 _yh = h2m1 if _xh - _xl >= vg4 and _yh - _yl >= vg4: _ds = Cnt_b1 contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) _xl = w2m1 _xh = w2m1 + dw2m1 contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) c.add_ref( gf.components.rectangle(size=(2 * wbulay, 2 * hbulay), layer=nBuLayer) ).move((-wbulay, -hbulay)) c.add_ref( gf.components.rectangle(size=(2 * wnwell, 2 * hnwell), layer=nwellLayer) ).move((-wnwell, -hnwell)) # Ring ref1 = c << gf.components.rectangle(size=(2 * w2psd, 2 * h2psd), layer=pSdLayer) ref1.move((-w2psd, -h2psd)) ref2 = c << gf.components.rectangle( size=(2 * w2psd + 2 * d2psd, 2 * h2psd + 2 * d2psd), layer=pSdLayer ) ref2.move((-w2psd - d2psd, -h2psd - d2psd)) c.add_ref( gf.boolean( ref2, ref1, operation="xor", layer=pSdLayer, layer1=pSdLayer, layer2=pSdLayer, ) ) # Delete the rectangle that was covering the whole region ref1.delete() # Delete the inner rectangle used for boolean ref2.delete() ref1 = c << gf.components.rectangle(size=(2 * w3act, 2 * h3act), layer=activLayer) ref1.move((-w3act, -h3act)) ref2 = c << gf.components.rectangle( size=(2 * w3act + 2 * d3act, 2 * h3act + 2 * d3act), layer=activLayer ) ref2.move((-w3act - d3act, -h3act - d3act)) c.add_ref( gf.boolean( ref2, ref1, operation="xor", layer=activLayer, layer1=activLayer, layer2=activLayer, ) ) # Delete the rectangle that was covering the whole region ref1.delete() # Delete the inner rectangle used for boolean ref2.delete() ref1 = c << gf.components.rectangle(size=(2 * w3act, 2 * h3act), layer=metal1Layer) ref1.move((-w3act, -h3act)) ref2 = c << gf.components.rectangle( size=(2 * w3act + 2 * d3act, 2 * h3act + 2 * d3act), layer=metal1Layer ) ref2.move((-w3act - d3act, -h3act - d3act)) c.add_ref( gf.boolean( ref2, ref1, operation="xor", layer=metal1Layer, layer1=metal1Layer, layer2=metal1Layer, ) ) # Delete the rectangle that was covering the whole region ref1.delete() # Delete the inner rectangle used for boolean ref2.delete() # Ring Metal MetT = True # include pins on top MetB = True # include pins on bottom MetL = True # include pins left MetR = True # include pins right _ds = Cnt_b _ox = 0.095 idtie = 0 if MetT: _xl = -w3act - d3act _xh = w3act + d3act _yl = h3act _yh = h3act + d3act contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) if idtie == 0: # Assigning reference to idtie, so that it is not used again in the next if statements. idtie = c << gf.components.rectangle( size=(2 * (w3act + d3act), d3act), layer=metal1_pin_Layer ) idtie.move((-w3act - d3act, h3act)) # Coordinates to be used for port idtie_xmin = -w3act - d3act idtie_xmax = idtie_xmin + 2 * (w3act + d3act) idtie_ymin = h3act idtie_ymax = idtie_ymin + d3act c.add_label(text="TIE", layer=textLayer, position=(0, h3act + d3act / 2)) if MetB: _xl = -w3act - d3act _xh = w3act + d3act _yl = -h3act - d3act _yh = -h3act contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) if idtie == 0: idtie = c << gf.components.rectangle( size=(2 * (w3act + d3act), d3act), layer=metal1_pin_Layer ) idtie.move((-w3act - d3act, -h3act - d3act)) # Coordinates to be used for port idtie_xmin = -w3act - d3act idtie_xmax = idtie_xmin + 2 * (w3act + d3act) idtie_ymin = -h3act - d3act idtie_ymax = idtie_ymin + d3act c.add_label(text="TIE", layer=textLayer, position=(0, -h3act - d3act / 2)) _oy = 0.085 if MetL: _xl = -w3act - d3act _xh = -w3act _yl = -h3act _yh = h3act contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) if idtie == 0: idtie = c << gf.components.rectangle( size=(d3act, 2 * h3act), layer=metal1_pin_Layer ) idtie.move((-w3act - d3act, -h3act)) # Coordinates to be used for port idtie_xmin = -w3act - d3act idtie_xmax = idtie_xmin + d3act idtie_ymin = -h3act idtie_ymax = idtie_ymin + 2 * h3act c.add_label(text="TIE", layer=textLayer, position=(-w3act - d3act / 2, 0)) if MetR: _xl = w3act _xh = w3act + d3act _yl = -h3act _yh = h3act contactArray( c, length=_xh - _xl, width=_yh - _yl, contactLayer=contLayer, xl=_xl, yl=_yl, ox=_ox, oy=_oy, ws=_ws, ds=_ds, ) if idtie == 0: idtie = c << gf.components.rectangle( size=(d3act, 2 * h3act), layer=metal1_pin_Layer ) idtie.move((w3act, -h3act)) # Coordinates to be used for port idtie_xmin = w3act idtie_xmax = idtie_xmin + d3act idtie_ymin = -h3act idtie_ymax = idtie_ymin + 2 * h3act c.add_label(text="TIE", layer=textLayer, position=(w3act + d3act / 2, 0)) c.add_ref( gf.components.rectangle(size=(2 * w1m1, 2 * h1m1), layer=metal1_pin_Layer) ).move((-w1m1, -h1m1)) c.add_ref( gf.components.rectangle(size=(2 * w1m1, 2 * h1m1), layer=metal1Layer) ).move((-w1m1, -h1m1)) c.add_ref( gf.components.rectangle(size=(dw2m1, 2 * h2m1), layer=metal1_pin_Layer) ).move((-w2m1 - dw2m1, -h2m1)) if idtie != 0: c.add_port( "TIE", center=(0.5 * (idtie_xmin + idtie_xmax), 0.5 * (idtie_ymin + idtie_ymax)), width=_snap_width_to_grid(idtie_ymax - idtie_ymin), layer=metal1_pin_Layer, orientation=180.0, port_type="electrical", ) c.add_port( "PLUS", center=(0, 0), width=_snap_width_to_grid(2 * w1m1), layer=metal1_pin_Layer, orientation=270.0, port_type="electrical", ) c.add_port( "MINUS", center=(-w2m1 - dw2m1 / 2, 0), width=_snap_width_to_grid(dw2m1), layer=metal1_pin_Layer, orientation=270.0, port_type="electrical", ) c.info["vlsir"] = { "model": "pnpMPA", "spice_type": "SUBCKT", "spice_lib": "sg13g2_hbt_mod.lib", "port_order": ["c", "b", "e"], "port_map": {"MINUS": "c", "TIE": "b", "PLUS": "e"}, } return c
if __name__ == "__main__": from gdsfactory.difftest import xor from ihp import PDK, cells2 PDK.activate() # c0 = cells2.npn13G2() # c1 = npn13G2() # c = xor(c0, c1) # c.show() # c0 = cells2.npn13G2L(Nx=2) # c1 = npn13G2L(Nx=2) # c = xor(c0, c1) # c.show() # c0 = cells2.npn13G2V(Nx=3) # c1 = npn13G2V(Nx=3) # c = xor(c0, c1) # c.show() c0 = cells2.pnpMPA() c1 = pnpMPA() c = xor(c0, c1) c.show()