Source code for sky130.pcells.npn_W1L2

from math import ceil

import gdsfactory as gf
from gdsfactory.typings import Float2, LayerSpec


[docs] @gf.cell def npn_W1L2( E_width: float = 1, E_length: float = 2, B_width: float = 0.4, C_width: float = 0.4, np_spacing: float = 0.27, diffusion_layer: LayerSpec = (65, 20), tap_layer: LayerSpec = (65, 44), diff_enclosure: Float2 = (0.18, 0.18), 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), pwell_layer: LayerSpec = (64, 13), dnwell_enclosure: Float2 = (0.4, 0.4), dnwell_layer: LayerSpec = (64, 18), sdm_enclosure: Float2 = (0.125, 0.125), nsdm_layer: LayerSpec = (93, 44), sdm_spacing: float = 0.13, psdm_layer: LayerSpec = (94, 20), npn_layer: LayerSpec = (82, 20), li_width: float = 0.17, li_spacing: float = 0.17, li_layer: LayerSpec = (67, 20), li_enclosure: float = 0.08, mcon_layer: LayerSpec = (67, 44), mcon_enclosure: Float2 = (0.09, 0.09), m1_layer: LayerSpec = (68, 20), ) -> gf.Component: """Return npn device with emitter size 1*2. .. plot:: :include-source: import sky130 c = sky130.pcells.npn_W1L2(B_width=0.8, C_width=0.8, np_spacing=1) c.plot() """ c = gf.Component() # generate emitter rect_E = gf.components.rectangle(size=(E_width, E_length), layer=diffusion_layer) E = c.add_ref(rect_E) # generate its n+ implant rect_nme = gf.components.rectangle( size=(E_width + 2 * sdm_enclosure[0], E_length + 2 * sdm_enclosure[1]), layer=nsdm_layer, ) nsdm_e = c.add_ref(rect_nme) nsdm_e.move((-sdm_enclosure[0], -sdm_enclosure[1])) # generate its contacts and local interconnects and mcon and metal1 rect_c = gf.components.rectangle(size=contact_size, layer=contact_layer) nr_e = ceil(E_length / (contact_size[1] + contact_spacing[1])) nc_e = ceil(E_width / (contact_size[0] + contact_spacing[0])) if ( E_width - nc_e * contact_size[0] - (nc_e - 1) * contact_spacing[0] ) / 2 < contact_enclosure[0]: nc_e -= 1 if ( E_length - nr_e * contact_size[1] - (nr_e - 1) * contact_spacing[1] ) / 2 < contact_enclosure[1]: nr_e -= 1 rect_mc = gf.components.rectangle(size=contact_size, layer=mcon_layer) rect_c_mc = [rect_c, rect_mc] for i in rect_c_mc: con_sp = ( contact_size[0] + contact_spacing[0], contact_size[1] + contact_spacing[1], ) cont_e_arr = c.add_array(i, rows=nr_e, columns=nc_e, spacing=con_sp) cont_e_arr.movex( (E_width - nc_e * contact_size[0] - (nc_e - 1) * contact_spacing[0]) / 2 ) cont_e_arr.movey( (E_length - nr_e * contact_size[1] - (nr_e - 1) * contact_spacing[1]) / 2 ) rect_eli = gf.components.rectangle( size=( nc_e * contact_size[0] + (nc_e - 1) * contact_spacing[0] + 2 * li_enclosure, nr_e * contact_size[1] + (nr_e - 1) * contact_spacing[1] + 2 * li_enclosure, ), layer=li_layer, ) li_e = c.add_ref(rect_eli) li_e.movex( ( E_width - nc_e * contact_size[0] - (nc_e - 1) * contact_spacing[0] - 2 * li_enclosure ) / 2 ) li_e.movey( ( E_length - nr_e * contact_size[1] - (nr_e - 1) * contact_spacing[1] - 2 * li_enclosure ) / 2 ) rect_em1 = gf.components.rectangle( size=( nc_e * contact_size[0] + (nc_e - 1) * contact_spacing[0] + 2 * mcon_enclosure[0], nr_e * contact_size[1] + (nr_e - 1) * contact_spacing[1] + 2 * mcon_enclosure[1], ), layer=m1_layer, ) m1_e = c.add_ref(rect_em1) m1_e.movex( ( E_width - nc_e * contact_size[0] - (nc_e - 1) * contact_spacing[0] - 2 * mcon_enclosure[0] ) / 2 ) m1_e.movey( ( E_length - nr_e * contact_size[1] - (nr_e - 1) * contact_spacing[1] - 2 * mcon_enclosure[1] ) / 2 ) # generate base rect_B_in = gf.components.rectangle( size=(E_width + 2 * np_spacing, E_length + 2 * np_spacing), layer=tap_layer ) rect_B_out = gf.components.rectangle( size=( E_width + 2 * np_spacing + 2 * B_width, E_length + 2 * np_spacing + 2 * B_width, ), layer=tap_layer, ) c_B = gf.Component() c_B.add_ref(rect_E) B_in = c_B.add_ref(rect_B_in) B_out = c_B.add_ref(rect_B_out) B_in.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) B_in.movex(E_width + np_spacing) B_out.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) B_out.movex(E_width + np_spacing + B_width) c.add_ref(gf.geometry.boolean(A=B_out, B=B_in, operation="A-B", layer=tap_layer)) # generate its p+ implants rect_pmB_in = gf.components.rectangle( size=( E_width + 2 * np_spacing - 2 * sdm_enclosure[0], E_length + 2 * np_spacing - 2 * sdm_enclosure[1], ), layer=psdm_layer, ) rect_pmB_out = gf.components.rectangle( size=( E_width + 2 * np_spacing + 2 * B_width + 2 * sdm_enclosure[0], E_length + 2 * np_spacing + 2 * B_width + 2 * sdm_enclosure[1], ), layer=psdm_layer, ) pmB_in = c_B.add_ref(rect_pmB_in) pmB_out = c_B.add_ref(rect_pmB_out) pmB_in.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) pmB_in.movex(E_width + np_spacing - sdm_enclosure[0]) pmB_out.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) pmB_out.movex(E_width + np_spacing + B_width + sdm_enclosure[1]) c.add_ref( gf.geometry.boolean(A=pmB_out, B=pmB_in, operation="A-B", layer=psdm_layer) ) # generate its contacts and local interconnects and mcon and metal1 nr_v = ceil((B_in.ymax - B_in.ymin) / (contact_size[1] + contact_spacing[1])) nc_v = ceil((B_width) / (contact_size[0] + contact_spacing[0])) if ( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) < contact_enclosure[0]: nc_v -= 1 if ( ( B_in.ymax - B_in.ymin - nr_v * contact_size[1] - (nr_v - 1) * contact_spacing[1] ) / 2 ) < contact_enclosure[1]: nr_v -= 1 nc_h = ceil((B_in.xmax - B_in.xmin) / (contact_size[0] + contact_spacing[0])) nr_h = ceil((B_width) / (contact_size[1] + contact_spacing[1])) if ( (B_width - nr_h * contact_size[0] - (nr_h - 1) * contact_spacing[0]) / 2 ) < contact_enclosure[0]: nr_h -= 1 if ( ( B_in.xmax - B_in.xmin - nc_h * contact_size[1] - (nc_h - 1) * contact_spacing[1] ) / 2 ) < contact_enclosure[1]: nc_h -= 1 rect_layer = [li_layer, m1_layer] for i in range(2): rect_in = gf.components.rectangle( size=( E_width + 2 * np_spacing + (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) - 2 * i * mcon_enclosure[0] - 2 * (1 - i) * li_enclosure, E_length + 2 * np_spacing + ( (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) - 2 * i * mcon_enclosure[1] - 2 * (1 - i) * li_enclosure ), ), layer=rect_layer[i], ) rect_out = gf.components.rectangle( size=( E_width + 2 * np_spacing + 2 * B_width - (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) + 2 * i * mcon_enclosure[0] + 2 * (1 - i) * li_enclosure, E_length + 2 * np_spacing + 2 * B_width - (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) + 2 * i * mcon_enclosure[1] + 2 * (1 - i) * li_enclosure, ), layer=rect_layer[i], ) li_m1_b_in = c_B.add_ref(rect_in) li_m1_b_out = c_B.add_ref(rect_out) li_m1_b_in.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) li_m1_b_in.movex( ( E_width + np_spacing + (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) - i * mcon_enclosure[0] - (1 - i) * li_enclosure ) li_m1_b_out.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) li_m1_b_out.movex( ( E_width + np_spacing + B_width - ( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) + i * mcon_enclosure[0] ) + (1 - i) * li_enclosure ) c.add_ref( gf.geometry.boolean( A=li_m1_b_out, B=li_m1_b_in, operation="A-B", layer=rect_layer[i] ) ) for i in rect_c_mc: cont_B_arr1 = c.add_array( i, rows=nr_v, columns=nc_v, spacing=con_sp ) # left side cont_B_arr1.move((-np_spacing - B_width, -np_spacing)) cont_B_arr1.movex( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) cont_B_arr1.movey( ( B_in.ymax - B_in.ymin - nr_v * contact_size[1] - (nr_v - 1) * contact_spacing[1] ) / 2 ) cont_B_arr2 = c.add_array( i, rows=nr_v, columns=nc_v, spacing=con_sp ) # right side cont_B_arr2.move((E_width + np_spacing, -np_spacing)) cont_B_arr2.movex( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) cont_B_arr2.movey( ( B_in.ymax - B_in.ymin - nr_v * contact_size[1] - (nr_v - 1) * contact_spacing[1] ) / 2 ) cont_B_arr3 = c.add_array( i, rows=nr_h, columns=nc_h, spacing=con_sp ) # upper side cont_B_arr3.move((-np_spacing, E_length + np_spacing)) cont_B_arr3.movex( ( B_in.xmax - B_in.xmin - nc_h * contact_size[0] - (nc_h - 1) * contact_spacing[0] ) / 2 ) cont_B_arr3.movey( (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2 ) cont_B_arr4 = c.add_array( i, rows=nr_h, columns=nc_h, spacing=con_sp ) # bottom side cont_B_arr4.move((-np_spacing, -np_spacing - B_width)) cont_B_arr4.movex( ( B_in.xmax - B_in.xmin - nc_h * contact_size[0] - (nc_h - 1) * contact_spacing[0] ) / 2 ) cont_B_arr4.movey( (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2 ) cont_B_arrc1 = c.add_array( i, rows=nr_h, columns=nc_v, spacing=con_sp ) # corners cont_B_arrc1.move((-np_spacing - B_width, -np_spacing - B_width)) cont_B_arrc1.move( ( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) cont_B_arrc2 = c.add_array(i, rows=nr_h, columns=nc_v, spacing=con_sp) cont_B_arrc2.move((-np_spacing - B_width, E_length + np_spacing)) cont_B_arrc2.move( ( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) cont_B_arrc3 = c.add_array(i, rows=nr_h, columns=nc_v, spacing=con_sp) cont_B_arrc3.move((E_width + np_spacing, -np_spacing - B_width)) cont_B_arrc3.move( ( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) cont_B_arrc4 = c.add_array(i, rows=nr_h, columns=nc_v, spacing=con_sp) cont_B_arrc4.move((E_width + np_spacing, E_length + np_spacing)) cont_B_arrc4.move( ( (B_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (B_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) # generate collector rect_C_in = gf.components.rectangle( size=( E_width + 4.5 * np_spacing + 2 * B_width, E_length + 4.5 * np_spacing + 2 * B_width, ), layer=tap_layer, ) rect_C_out = gf.components.rectangle( size=( E_width + 4.5 * np_spacing + 2 * B_width + 2 * C_width, E_length + 4.5 * np_spacing + 2 * B_width + 2 * C_width, ), layer=tap_layer, ) c_C = gf.Component() c_C.add_ref(rect_E) C_in = c_C.add_ref(rect_C_in) C_out = c_C.add_ref(rect_C_out) C_in.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) C_in.movex(E_width + 2.25 * np_spacing + B_width) C_out.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) C_out.movex(E_width + 2.25 * np_spacing + B_width + C_width) c.add_ref(gf.geometry.boolean(A=C_out, B=C_in, operation="A-B", layer=tap_layer)) # generate its n+ implants rect_nmC_in = gf.components.rectangle( size=( E_width + 4.5 * np_spacing + 2 * B_width - 2 * sdm_enclosure[0], E_length + 4.5 * np_spacing + 2 * B_width - 2 * sdm_enclosure[1], ), layer=nsdm_layer, ) rect_nmC_out = gf.components.rectangle( size=( E_width + 4.5 * np_spacing + 2 * B_width + 2 * C_width + 2 * sdm_enclosure[0], E_length + 4.5 * np_spacing + 2 * B_width + 2 * C_width + 2 * sdm_enclosure[1], ), layer=nsdm_layer, ) nmC_in = c_C.add_ref(rect_nmC_in) nmC_out = c_C.add_ref(rect_nmC_out) nmC_in.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) nmC_in.movex(E_width + 2.25 * np_spacing + B_width - sdm_enclosure[0]) nmC_out.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) nmC_out.movex(E_width + 2.25 * np_spacing + B_width + C_width + sdm_enclosure[0]) c.add_ref( gf.geometry.boolean(A=nmC_out, B=nmC_in, operation="A-B", layer=nsdm_layer) ) # generate its contact and local interconnects nr_v = ceil((C_in.ymax - C_in.ymin) / (contact_size[1] + contact_spacing[1])) nc_v = ceil((C_width) / (contact_size[0] + contact_spacing[0])) if ( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) < contact_enclosure[0]: nc_v -= 1 if ( ( C_in.ymax - C_in.ymin - nr_v * contact_size[1] - (nr_v - 1) * contact_spacing[1] ) / 2 ) < contact_enclosure[1]: nr_v -= 1 nc_h = ceil((C_in.xmax - C_in.xmin) / (contact_size[0] + contact_spacing[0])) nr_h = ceil((C_width) / (contact_size[1] + contact_spacing[1])) if ( (C_width - nr_h * contact_size[0] - (nr_h - 1) * contact_spacing[1]) / 2 ) < contact_enclosure[1]: nr_h -= 1 if ( ( C_in.xmax - C_in.xmin - nc_h * contact_size[1] - (nc_h - 1) * contact_spacing[0] ) / 2 ) < contact_enclosure[0]: nc_h -= 1 for i in range(2): rect_in = gf.components.rectangle( size=( E_width + 4.5 * np_spacing + 2 * B_width + (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) - 2 * i * mcon_enclosure[0] - 2 * (1 - i) * li_enclosure, E_length + 4.5 * np_spacing + 2 * B_width + (C_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) - 2 * i * mcon_enclosure[1] - 2 * (1 - i) * li_enclosure, ), layer=rect_layer[i], ) rect_out = gf.components.rectangle( size=( E_width + 4.5 * np_spacing + 2 * B_width + 2 * C_width - (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) + 2 * i * mcon_enclosure[0] + 2 * (1 - i) * li_enclosure, E_length + 4.5 * np_spacing + 2 * B_width + 2 * C_width - (C_width - nr_h * contact_size[0] - (nr_h - 1) * contact_spacing[0]) + 2 * i * mcon_enclosure[1] + 2 * (1 - i) * li_enclosure, ), layer=rect_layer[i], ) li_m1_c_in = c_C.add_ref(rect_in) li_m1_c_out = c_C.add_ref(rect_out) li_m1_c_in.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) li_m1_c_in.movex( E_width + 2.25 * np_spacing + B_width + (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 - i * mcon_enclosure[0] - (1 - i) * li_enclosure ) li_m1_c_out.connect("e1", destination=E.ports["e1"], allow_layer_mismatch=True) li_m1_c_out.movex( E_width + 2.25 * np_spacing + B_width + C_width - (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 + i * mcon_enclosure[0] + (1 - i) * li_enclosure ) c.add_ref( gf.geometry.boolean( A=li_m1_c_out, B=li_m1_c_in, operation="A-B", layer=rect_layer[i] ) ) for i in rect_c_mc: cont_C_arr1 = c.add_array( i, rows=nr_v, columns=nc_v, spacing=con_sp ) # left side cont_C_arr1.move( (-2.25 * np_spacing - B_width - C_width, -2.25 * np_spacing - B_width) ) cont_C_arr1.movex( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) cont_C_arr1.movey( ( C_in.ymax - C_in.ymin - nr_v * contact_size[1] - (nr_v - 1) * contact_spacing[1] ) / 2 ) cont_C_arr2 = c.add_array( i, rows=nr_v, columns=nc_v, spacing=con_sp ) # right side cont_C_arr2.move( (E_width + 2.25 * np_spacing + B_width, -2.25 * np_spacing - B_width) ) cont_C_arr2.movex( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2 ) cont_C_arr2.movey( ( C_in.ymax - C_in.ymin - nr_v * contact_size[1] - (nr_v - 1) * contact_spacing[1] ) / 2 ) cont_C_arr3 = c.add_array( i, rows=nr_h, columns=nc_h, spacing=con_sp ) # upper side cont_C_arr3.move( (-2.25 * np_spacing - B_width, E_length + 2.25 * np_spacing + B_width) ) cont_C_arr3.movex( ( C_in.xmax - C_in.xmin - nc_h * contact_size[0] - (nc_h - 1) * contact_spacing[0] ) / 2 ) cont_C_arr3.movey( (C_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2 ) cont_C_arr4 = c.add_array( i, rows=nr_h, columns=nc_h, spacing=con_sp ) # bottom side cont_C_arr4.move( (-2.25 * np_spacing - B_width, -2.25 * np_spacing - B_width - C_width) ) cont_C_arr4.movex( ( C_in.xmax - C_in.xmin - nc_h * contact_size[0] - (nc_h - 1) * contact_spacing[0] ) / 2 ) cont_C_arr4.movey( (C_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2 ) cont_C_arrc1 = c.add_array( i, rows=nr_h, columns=nc_v, spacing=con_sp ) # corners cont_C_arrc1.move( ( -2.25 * np_spacing - B_width - C_width, -2.25 * np_spacing - B_width - C_width, ) ) cont_C_arrc1.move( ( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (C_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) cont_C_arrc2 = c.add_array( i, rows=nr_h, columns=nc_v, spacing=con_sp ) # corners cont_C_arrc2.move( ( -2.25 * np_spacing - B_width - C_width, E_length + 2.25 * np_spacing + B_width, ) ) cont_C_arrc2.move( ( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (C_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) cont_C_arrc3 = c.add_array( i, rows=nr_h, columns=nc_v, spacing=con_sp ) # corners cont_C_arrc3.move( ( E_width + 2.25 * np_spacing + B_width, E_length + 2.25 * np_spacing + B_width, ) ) cont_C_arrc3.move( ( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (C_width - nr_h * contact_size[1] - (nr_h - 1) * contact_spacing[1]) / 2, ) ) cont_C_arrc4 = c.add_array( i, rows=nr_h, columns=nc_v, spacing=con_sp ) # corners cont_C_arrc4.move( ( E_width + 2.25 * np_spacing + B_width, -2.25 * np_spacing - B_width - C_width, ) ) cont_C_arrc4.move( ( (C_width - nc_v * contact_size[0] - (nc_v - 1) * contact_spacing[0]) / 2, (C_width - nc_v * contact_size[1] - (nc_v - 1) * contact_spacing[1]) / 2, ) ) # generating pwell around E & B rect_pwell = gf.components.rectangle( size=( B_out.xmax - B_out.xmin + 2 * diff_enclosure[0], B_out.ymax - B_out.ymin + 2 * diff_enclosure[1], ), layer=pwell_layer, ) pwell = c.add_ref(rect_pwell) pwell.connect("e1", destination=B_out.ports["e3"], allow_layer_mismatch=True) pwell.movex(B_out.xmax - B_out.xmin + diff_enclosure[0]) # generating deep nwell rect_dnw = gf.components.rectangle( size=( C_out.xmax - C_out.xmin + 2 * diff_enclosure[0], C_out.ymax - C_out.ymin + 2 * diff_enclosure[1], ), layer=dnwell_layer, ) dnwell = c.add_ref(rect_dnw) dnwell.connect("e1", destination=C_out.ports["e3"], allow_layer_mismatch=True) dnwell.movex(C_out.xmax - C_out.xmin + diff_enclosure[0]) # generating npn identifier npn = c.add_ref( gf.components.rectangle( size=(C_out.xmax - C_out.xmin, C_out.ymax - C_out.ymin), layer=npn_layer ) ) npn.connect("e1", destination=C_out.ports["e3"], allow_layer_mismatch=True) npn.movex(C_out.xmax - C_out.xmin) return c
if __name__ == "__main__": # c=npn_W1L2() c = npn_W1L2(B_width=0.8, C_width=0.8, np_spacing=1) c.show(show_ports=True)