Source code for ihp.tech

"""IHP PDK Technology definitions.

- LayerMap with IHP PDK layers
- LayerStack for 3D representation
- Cross-sections for routing
- Technology parameters
"""

import sys
from functools import partial
from typing import Any

import gdsfactory as gf
from doroutes.bundles import add_bundle_astar
from gdsfactory import typings
from gdsfactory.component import Component
from gdsfactory.cross_section import (
    CrossSection,
    get_cross_sections,
    port_names_electrical,
    port_types_electrical,
    xsection,
)
from gdsfactory.technology import LayerLevel, LayerMap, LayerStack
from gdsfactory.typings import Layer, LayerSpec
from pydantic import BaseModel

# Import CNI tech for cells2 compatibility
from cni.tech import Tech as _CNITech
from ihp.config import PATH

nm = 1e-3
pin_length = 10 * nm
heater_width = 4


[docs] class LayerMapIHP(LayerMap): ActivOPC: Layer = (1, 26) Activboundary: Layer = (1, 4) Activdrawing: Layer = (1, 0) Activfiller: Layer = (1, 22) ActiviOPC: Layer = (1, 27) Activlabel: Layer = (1, 1) Activlvs: Layer = (1, 19) Activmask: Layer = (1, 20) Activnet: Layer = (1, 3) Activnofill: Layer = (1, 23) Activnoqrc: Layer = (1, 28) Activpin: Layer = (1, 2) AlCuStopdrawing: Layer = (159, 0) AntMetal1drawing: Layer = (132, 0) AntMetal2drawing: Layer = (84, 0) AntVia1drawing: Layer = (83, 0) BackMetal1OPC: Layer = (20, 26) BackMetal1boundary: Layer = (20, 4) BackMetal1diffprb: Layer = (20, 34) BackMetal1drawing: Layer = (20, 0) BackMetal1filler: Layer = (20, 22) BackMetal1iprobe: Layer = (20, 33) BackMetal1label: Layer = (20, 1) BackMetal1mask: Layer = (20, 20) BackMetal1net: Layer = (20, 3) BackMetal1nofill: Layer = (20, 23) BackMetal1noqrc: Layer = (20, 28) BackMetal1pin: Layer = (20, 2) BackMetal1res: Layer = (20, 29) BackMetal1slit: Layer = (20, 24) BackMetal1text: Layer = (20, 25) BackPassivdrawing: Layer = (23, 0) BasPolyboundary: Layer = (13, 4) BasPolydrawing: Layer = (13, 0) BasPolylabel: Layer = (13, 1) BasPolynet: Layer = (13, 3) BasPolypin: Layer = (13, 2) BiWindOPC: Layer = (3, 26) BiWinddrawing: Layer = (3, 0) ColOpendrawing: Layer = (101, 0) ColWinddrawing: Layer = (139, 0) ContOPC: Layer = (6, 26) Contboundary: Layer = (6, 4) Contdrawing: Layer = (6, 0) Contnet: Layer = (6, 3) CtrGatdrawing: Layer = (154, 0) DeepCodrawing: Layer = (35, 0) DeepViadrawing: Layer = (152, 0) DevBondMetdrawing: Layer = (75, 0) DevBondViadrawing: Layer = (74, 0) DevTrenchdrawing: Layer = (76, 0) DigiBnddrawing: Layer = (16, 0) DigiBnddrawing0: Layer = (16, 10) DigiSubdrawing: Layer = (60, 0) EXTBlockdrawing: Layer = (111, 0) EdgeSealboundary: Layer = (39, 4) EdgeSealdrawing: Layer = (39, 0) EmPolydrawing: Layer = (55, 0) EmWiHV3drawing: Layer = (91, 0) EmWiHVdrawing: Layer = (156, 0) EmWind3drawing: Layer = (90, 0) EmWindOPC: Layer = (33, 26) EmWinddrawing: Layer = (33, 0) Exchange0drawing: Layer = (190, 0) Exchange0label: Layer = (190, 1) Exchange0pin: Layer = (190, 2) Exchange0text: Layer = (190, 25) Exchange1drawing: Layer = (191, 0) Exchange1label: Layer = (191, 1) Exchange1pin: Layer = (191, 2) Exchange1text: Layer = (191, 25) Exchange2drawing: Layer = (192, 0) Exchange2label: Layer = (192, 1) Exchange2pin: Layer = (192, 2) Exchange2text: Layer = (192, 25) Exchange3drawing: Layer = (193, 0) Exchange3label: Layer = (193, 1) Exchange3pin: Layer = (193, 2) Exchange3text: Layer = (193, 25) Exchange4drawing: Layer = (194, 0) Exchange4label: Layer = (194, 1) Exchange4pin: Layer = (194, 2) Exchange4text: Layer = (194, 25) FBEdrawing: Layer = (54, 0) FGEtchdrawing: Layer = (153, 0) FGImpdrawing: Layer = (155, 0) FLMdrawing: Layer = (142, 0) GatPolyOPC: Layer = (5, 26) GatPolyboundary: Layer = (5, 4) GatPolydrawing: Layer = (5, 0) GatPolyfiller: Layer = (5, 22) GatPolyiOPC: Layer = (5, 27) GatPolylabel: Layer = (5, 1) GatPolynet: Layer = (5, 3) GatPolynofill: Layer = (5, 23) GatPolynoqrc: Layer = (5, 28) GatPolypin: Layer = (5, 2) GraphBotdrawing: Layer = (78, 0) GraphContdrawing: Layer = (85, 0) GraphGatedrawing: Layer = (118, 0) GraphMet1LOPC: Layer = (110, 26) GraphMet1Ldrawing: Layer = (110, 0) GraphMet1Lfiller: Layer = (110, 22) GraphMet1Lnofill: Layer = (110, 23) GraphMet1Lslit: Layer = (110, 24) GraphMetal1OPC: Layer = (109, 26) GraphMetal1drawing: Layer = (109, 0) GraphMetal1filler: Layer = (109, 22) GraphMetal1nofill: Layer = (109, 23) GraphMetal1slit: Layer = (109, 24) GraphPaddrawing: Layer = (97, 0) GraphPasdrawing: Layer = (89, 0) GraphTopdrawing: Layer = (79, 0) HafniumOxdrawing: Layer = (143, 0) HeatResdrawing: Layer = (52, 0) HeatTransdrawing: Layer = (51, 0) ICdrawing: Layer = (48, 0) INDboundary: Layer = (27, 4) INDdrawing: Layer = (27, 0) INDpin: Layer = (27, 2) INDtext: Layer = (27, 25) INLDPWLdrawing: Layer = (127, 0) IntBondMetdrawing: Layer = (73, 0) IntBondViadrawing: Layer = (72, 0) LBEdrawing: Layer = (157, 0) MEMPADdrawing: Layer = (124, 0) MEMViadrawing: Layer = (145, 0) MIMboundary: Layer = (36, 4) MIMdrawing: Layer = (36, 0) MIMnet: Layer = (36, 3) MemCapdrawing: Layer = (69, 0) Metal1OPC: Layer = (8, 26) Metal1boundary: Layer = (8, 4) Metal1diffprb: Layer = (8, 34) Metal1drawing: Layer = (8, 0) Metal1filler: Layer = (8, 22) Metal1iprobe: Layer = (8, 33) Metal1label: Layer = (8, 1) Metal1mask: Layer = (8, 20) Metal1net: Layer = (8, 3) Metal1nofill: Layer = (8, 23) Metal1noqrc: Layer = (8, 28) Metal1pin: Layer = (8, 2) Metal1res: Layer = (8, 29) Metal1slit: Layer = (8, 24) Metal1text: Layer = (8, 25) Metal2OPC: Layer = (10, 26) Metal2boundary: Layer = (10, 4) Metal2diffprb: Layer = (10, 34) Metal2drawing: Layer = (10, 0) Metal2filler: Layer = (10, 22) Metal2iprobe: Layer = (10, 33) Metal2label: Layer = (10, 1) Metal2mask: Layer = (10, 20) Metal2net: Layer = (10, 3) Metal2nofill: Layer = (10, 23) Metal2noqrc: Layer = (10, 28) Metal2pin: Layer = (10, 2) Metal2res: Layer = (10, 29) Metal2slit: Layer = (10, 24) Metal2text: Layer = (10, 25) Metal3OPC: Layer = (30, 26) Metal3boundary: Layer = (30, 4) Metal3diffprb: Layer = (30, 34) Metal3drawing: Layer = (30, 0) Metal3filler: Layer = (30, 22) Metal3iprobe: Layer = (30, 33) Metal3label: Layer = (30, 1) Metal3mask: Layer = (30, 20) Metal3net: Layer = (30, 3) Metal3nofill: Layer = (30, 23) Metal3noqrc: Layer = (30, 28) Metal3pin: Layer = (30, 2) Metal3res: Layer = (30, 29) Metal3slit: Layer = (30, 24) Metal3text: Layer = (30, 25) Metal4OPC: Layer = (50, 26) Metal4boundary: Layer = (50, 4) Metal4diffprb: Layer = (50, 34) Metal4drawing: Layer = (50, 0) Metal4filler: Layer = (50, 22) Metal4iprobe: Layer = (50, 33) Metal4label: Layer = (50, 1) Metal4mask: Layer = (50, 20) Metal4net: Layer = (50, 3) Metal4nofill: Layer = (50, 23) Metal4noqrc: Layer = (50, 28) Metal4pin: Layer = (50, 2) Metal4res: Layer = (50, 29) Metal4slit: Layer = (50, 24) Metal4text: Layer = (50, 25) Metal5OPC: Layer = (67, 26) Metal5boundary: Layer = (67, 4) Metal5diffprb: Layer = (67, 34) Metal5drawing: Layer = (67, 0) Metal5filler: Layer = (67, 22) Metal5iprobe: Layer = (67, 33) Metal5label: Layer = (67, 1) Metal5mask: Layer = (67, 20) Metal5net: Layer = (67, 3) Metal5nofill: Layer = (67, 23) Metal5noqrc: Layer = (67, 28) Metal5pin: Layer = (67, 2) Metal5res: Layer = (67, 29) Metal5slit: Layer = (67, 24) Metal5text: Layer = (67, 25) NExtHVdrawing: Layer = (116, 0) NExtdrawing: Layer = (114, 0) NLDBdrawing: Layer = (15, 0) NLDDdrawing: Layer = (112, 0) NWellboundary: Layer = (31, 4) NWelldrawing: Layer = (31, 0) NWelllabel: Layer = (31, 1) NWellnet: Layer = (31, 3) NWellpin: Layer = (31, 2) NoDRCdrawing: Layer = (62, 0) NoMetFillerdrawing: Layer = (160, 0) NoRCXdrawing: Layer = (148, 0) NoRCXm1sub: Layer = (148, 123) NoRCXm2m3: Layer = (148, 41) NoRCXm2m4: Layer = (148, 42) NoRCXm2m5: Layer = (148, 43) NoRCXm2sub: Layer = (148, 124) NoRCXm2tm1: Layer = (148, 44) NoRCXm2tm2: Layer = (148, 45) NoRCXm3m4: Layer = (148, 46) NoRCXm3m5: Layer = (148, 47) NoRCXm3sub: Layer = (148, 125) NoRCXm3tm1: Layer = (148, 48) NoRCXm3tm2: Layer = (148, 49) NoRCXm4m5: Layer = (148, 50) NoRCXm4sub: Layer = (148, 126) NoRCXm4tm1: Layer = (148, 51) NoRCXm4tm2: Layer = (148, 52) NoRCXm5sub: Layer = (148, 127) NoRCXm5tm1: Layer = (148, 53) NoRCXm5tm2: Layer = (148, 54) NoRCXtm1sub: Layer = (148, 300) NoRCXtm1tm2: Layer = (148, 55) NoRCXtm2sub: Layer = (148, 301) PExtHVdrawing: Layer = (117, 0) PExtdrawing: Layer = (115, 0) PLDBdrawing: Layer = (45, 0) PLDDdrawing: Layer = (113, 0) PWellblock: Layer = (46, 21) PWellboundary: Layer = (46, 4) PWelldrawing: Layer = (46, 0) PWelllabel: Layer = (46, 1) PWellnet: Layer = (46, 3) PWellpin: Layer = (46, 2) Passivboundary: Layer = (9, 4) Passivdrawing: Layer = (9, 0) Passivlabel: Layer = (9, 1) Passivnet: Layer = (9, 3) Passivpdl: Layer = (9, 40) Passivpillar: Layer = (9, 35) Passivpin: Layer = (9, 2) Passivsbump: Layer = (9, 36) Polimidedrawing: Layer = (98, 0) Polimidelabel: Layer = (98, 1) Polimidenet: Layer = (98, 3) Polimidepin: Layer = (98, 2) PolyResboundary: Layer = (128, 4) PolyResdrawing: Layer = (128, 0) PolyReslabel: Layer = (128, 1) PolyResnet: Layer = (128, 3) PolyRespin: Layer = (128, 2) RESdrawing: Layer = (24, 0) RESlabel: Layer = (24, 1) RFMEMdrawing: Layer = (147, 0) RadHarddrawing: Layer = (68, 0) Recogdiffprb: Layer = (99, 34) Recogdiode: Layer = (99, 31) Recogdrawing: Layer = (99, 0) Recogesd: Layer = (99, 30) Recogiprobe: Layer = (99, 33) Recogmom: Layer = (99, 39) Recogotp: Layer = (99, 37) Recogpcm: Layer = (99, 100) Recogpdiode: Layer = (99, 38) Recogpillar: Layer = (99, 35) Recogpin: Layer = (99, 2) Recogsbump: Layer = (99, 36) Recogtsv: Layer = (99, 32) RedBuLaydrawing: Layer = (92, 0) Redistdrawing: Layer = (77, 0) SMOSdrawing: Layer = (93, 0) SNSArmsdrawing: Layer = (137, 0) SNSBotViadrawing: Layer = (149, 0) SNSCMOSViadrawing: Layer = (138, 0) SNSRingdrawing: Layer = (135, 0) SNSTopViadrawing: Layer = (151, 0) SRAMboundary: Layer = (25, 4) SRAMdrawing: Layer = (25, 0) SRAMlabel: Layer = (25, 1) SalBlockdrawing: Layer = (28, 0) Sensordrawing: Layer = (136, 0) SiGratingdrawing: Layer = (87, 0) SiNGratingdrawing: Layer = (88, 0) SiNWGdrawing: Layer = (119, 0) SiNWGfiller: Layer = (119, 22) SiNWGnofill: Layer = (119, 23) SiWGdrawing: Layer = (86, 0) SiWGfiller: Layer = (86, 22) SiWGnofill: Layer = (86, 23) Substratedrawing: Layer = (40, 0) Substratetext: Layer = (40, 25) TEXTdrawing: Layer = (63, 0) TRANSdrawing: Layer = (26, 0) ThickGateOxdrawing: Layer = (44, 0) ThinFilmResdrawing: Layer = (146, 0) TopMetal1boundary: Layer = (126, 4) TopMetal1diffprb: Layer = (126, 34) TopMetal1drawing: Layer = (126, 0) TopMetal1filler: Layer = (126, 22) TopMetal1iprobe: Layer = (126, 33) TopMetal1label: Layer = (126, 1) TopMetal1mask: Layer = (126, 20) TopMetal1net: Layer = (126, 3) TopMetal1nofill: Layer = (126, 23) TopMetal1noqrc: Layer = (126, 28) TopMetal1pin: Layer = (126, 2) TopMetal1res: Layer = (126, 29) TopMetal1slit: Layer = (126, 24) TopMetal1text: Layer = (126, 25) TopMetal2boundary: Layer = (134, 4) TopMetal2diffprb: Layer = (134, 34) TopMetal2drawing: Layer = (134, 0) TopMetal2filler: Layer = (134, 22) TopMetal2iprobe: Layer = (134, 33) TopMetal2label: Layer = (134, 1) TopMetal2mask: Layer = (134, 20) TopMetal2net: Layer = (134, 3) TopMetal2nofill: Layer = (134, 23) TopMetal2noqrc: Layer = (134, 28) TopMetal2pin: Layer = (134, 2) TopMetal2res: Layer = (134, 29) TopMetal2slit: Layer = (134, 24) TopMetal2text: Layer = (134, 25) TopVia1boundary: Layer = (125, 4) TopVia1drawing: Layer = (125, 0) TopVia1net: Layer = (125, 3) TopVia2boundary: Layer = (133, 4) TopVia2drawing: Layer = (133, 0) TopVia2net: Layer = (133, 3) Varicapdrawing: Layer = (70, 0) Via1boundary: Layer = (19, 4) Via1drawing: Layer = (19, 0) Via1net: Layer = (19, 3) Via2boundary: Layer = (29, 4) Via2drawing: Layer = (29, 0) Via2net: Layer = (29, 3) Via3boundary: Layer = (49, 4) Via3drawing: Layer = (49, 0) Via3net: Layer = (49, 3) Via4boundary: Layer = (66, 4) Via4drawing: Layer = (66, 0) Via4net: Layer = (66, 3) Vmimdrawing: Layer = (129, 0) dfpaddrawing: Layer = (41, 0) dfpadpillar: Layer = (41, 35) dfpadsbump: Layer = (41, 36) isoNWelldrawing: Layer = (257, 0) nBuLayCutdrawing: Layer = (131, 0) nBuLayblock: Layer = (32, 21) nBuLayboundary: Layer = (32, 4) nBuLaydrawing: Layer = (32, 0) nBuLaylabel: Layer = (32, 1) nBuLaynet: Layer = (32, 3) nBuLaypin: Layer = (32, 2) nSDblock: Layer = (7, 21) nSDdrawing: Layer = (7, 0) pSDdrawing: Layer = (14, 0) prBoundaryboundary: Layer = (189, 4) prBoundarydrawing: Layer = (189, 0) prBoundarylabel: Layer = (189, 1)
LAYER = LayerMapIHP def add_labels_to_ports_optical( component: Component, label_layer: LayerSpec = LAYER.TEXTdrawing, port_type: str | None = "optical", **kwargs, ) -> Component: """Add labels to component ports. Args: component: to add labels. label_layer: layer spec for the label. port_type: to select ports. keyword Args: layer: select ports with GDS layer. prefix: select ports with prefix in port name. orientation: select ports with orientation in degrees. width: select ports with port width. layers_excluded: List of layers to exclude. port_type: select ports with port_type (optical, electrical, vertical_te). clockwise: if True, sort ports clockwise, False: counter-clockwise. """ suffix = "o3_0" if len(component.ports) == 4 else "o2_0" ports = component.ports.filter(port_type=port_type, suffix=suffix, **kwargs) for port in ports: component.add_label(text=port.name, position=port.center, layer=label_layer) return component margin = 0.5 def get_routing_stack() -> LayerStack: """Returns IHP PDK LayerStack with only metallization and GatPoly layers. Subset of get_layer_stack() for routing and interconnect visualization. """ _routing_keys = { "poly" : "GatPoly", "metal1" : "Metal1", "metal2" : "Metal2", "metal3" : "Metal3", "metal4" : "Metal4", "metal5" : "Metal5", "topmetal1" : "TopMetal1", "topmetal2" : "TopMetal2", } full = get_layer_stack() return LayerStack( layers={_routing_keys[k]: v for k, v in full.layers.items() if k in _routing_keys.keys()} )
[docs] def get_layer_stack( thickness_gatpoly: float = 0.16, # GatPoly thickness (160nm from process spec) thickness_cont: float = 0.64, # Activ-M1 Cont thickness (640nm from process spec) thickness_metal1: float = 0.42, # Metal1 thickness (420 nm from process specs) thickness_metal: float = 0.49, # Metal2-5 thickness (490 nm from process specs) thickness_via: float = 0.54, # Via1-4 thickness (540 nm from process specs) thickness_topvia1: float = 0.85, # TopVia1 thickness (850 nm from process specs) thickness_topmetal1: float = 2.0, # TopMetal1 thickness (2000 nm from process specs) thickness_topvia2: float = 2.8, # TopVia2 thickness (2800 nm from process specs) thickness_topmetal2: float = 3.0, # TopMetal2 thickness (3000 nm from process specs) ) -> LayerStack: """Returns IHP PDK LayerStack for 3D visualization and simulation. Layer thicknesses are based on the IHP SG13 process specifications. Reference: https://ihp-open-pdk-docs.readthedocs.io/en/latest/process_specs/01_01_main_process_cross_sec.html Args: thickness_gatepoly: Gate Oxide thickness in um (default: 0.16) thickness_cont: Activ-Metal1 Cont thickness in um (default: 0.64) thickness_metal1: Metal1 layer thickness in um (default: 0.42). thickness_metal: Metal2-5 layer thickness in um (default: 0.49). thickness_via: Via1-4 layer thickness in um (default: 0.54). thickness_topvia1: TopVia1 layer thickness in um (default: 0.85). thickness_topmetal1: TopMetal1 layer thickness in um (default: 2.0). thickness_topvia2: TopVia2 layer thickness in um (default: 2.8). thickness_topmetal2: TopMetal2 layer thickness in um (default: 3.0). Returns: LayerStack for IHP PDK with properly connected metal and via layers. """ # Common z-reference: active surface at z=0.2 z_active_top = 0.2 # BEOL base: top of cont layer z_m1 = z_active_top + thickness_cont z_beol = z_m1 + thickness_metal1 # top of Metal1 # Accumulate BEOL z for Metal2-5 z_m5_top = ( z_beol + 4 * thickness_via # Via1-4 + 3 * thickness_metal # Metal2-4 + thickness_metal ) # Metal5 z_tv1_top = z_m5_top + thickness_topvia1 z_tm1_top = z_tv1_top + thickness_topmetal1 z_tv2_top = z_tm1_top + thickness_topvia2 z_tm2_top = z_tv2_top + thickness_topmetal2 return LayerStack( layers=dict( # ============ Sub-surface ============ # PWell - p-type well implant pwell=LayerLevel( layer=LAYER.PWelldrawing, thickness=1.5, zmin=-1.32, material="si", info={"mesh_order": 97}, ), # NWell - deep n-type well implant (for pmos) nwell=LayerLevel( layer=LAYER.NWelldrawing, thickness=1.5, zmin=-1.30, material="si", info={"mesh_order": 98}, ), # ============ Active surface ============ # Active silicon active=LayerLevel( layer=LAYER.Activdrawing, thickness=0.201, zmin=0.0, material="si", info={"mesh_order": 1}, ), # nSD - n+ source/drain implant (shallow, jittered +0.01 above active base) nsd=LayerLevel( layer=LAYER.nSDdrawing, thickness=0.2, zmin=0.0, material="si", info={"mesh_order": 2}, ), # pSD - p+ source/drain implant (shallow, jittered +0.02) psd=LayerLevel( layer=LAYER.pSDdrawing, thickness=0.199, zmin=0.0, material="si", info={"mesh_order": 3}, ), # SalBlock - salicide block (thin marker on active surface) salblock=LayerLevel( layer=LAYER.SalBlockdrawing, thickness=0.02, zmin=z_active_top, material="sin", info={"mesh_order": 4}, ), # ThickGateOx - HV gate oxide ~7.3nm (TGOX1NW/TGOX1PW) thickgateox=LayerLevel( layer=LAYER.ThickGateOxdrawing, thickness=0.0073, zmin=z_active_top, material="sio2", info={"mesh_order": 5}, ), # ============ Poly / emitter ============ # GatPoly - polysilicon gate poly=LayerLevel( layer=LAYER.GatPolydrawing, thickness=thickness_gatpoly, zmin=z_active_top, material="poly_si", info={"mesh_order": 6}, ), # PolyRes - polysilicon resistor body (same z as gate poly) polyres=LayerLevel( layer=LAYER.PolyResdrawing, thickness=thickness_gatpoly, zmin=z_active_top, material="poly_si", info={"mesh_order": 7}, ), # EXTBlock - extension implant block (marker on poly) extblock=LayerLevel( layer=LAYER.EXTBlockdrawing, thickness=0.02, zmin=z_active_top + thickness_gatpoly, material="sin", info={"mesh_order": 8}, ), # EmWind - bipolar emitter window (on poly surface) emwind=LayerLevel( layer=LAYER.EmWinddrawing, thickness=0.03, zmin=z_active_top + thickness_gatpoly, material="poly_si", info={"mesh_order": 9}, ), # EmWiHV - bipolar HV emitter window emwihv=LayerLevel( layer=LAYER.EmWiHVdrawing, thickness=0.03, zmin=z_active_top + thickness_gatpoly, material="poly_si", info={"mesh_order": 10}, ), # HeatTrans - heater/thermal layer (on top of poly) heattrans=LayerLevel( layer=LAYER.HeatTransdrawing, thickness=0.05, zmin=z_active_top + thickness_gatpoly, material="TiN", info={"mesh_order": 11}, ), # HeatRes - heater resistor body heatres=LayerLevel( layer=LAYER.HeatResdrawing, thickness=0.05, zmin=z_active_top + thickness_gatpoly, material="TiN", info={"mesh_order": 12}, ), # ============ Contact ============ # Cont - tungsten contact plugs (Activ/Poly to Metal1) cont=LayerLevel( layer=LAYER.Contdrawing, thickness=thickness_cont, zmin=z_active_top, material="tungsten", info={"mesh_order": 13}, ), # ============ BEOL metals and vias ============ # Metal 1 metal1=LayerLevel( layer=LAYER.Metal1drawing, thickness=thickness_metal1, zmin=z_m1, material="aluminum", info={"mesh_order": 14}, ), # Via 1 via1=LayerLevel( layer=LAYER.Via1drawing, thickness=thickness_via, zmin=z_beol, material="tungsten", info={"mesh_order": 15}, ), # Metal 2 metal2=LayerLevel( layer=LAYER.Metal2drawing, thickness=thickness_metal, zmin=z_beol + thickness_via, material="aluminum", info={"mesh_order": 16}, ), # Via 2 via2=LayerLevel( layer=LAYER.Via2drawing, thickness=thickness_via, zmin=z_beol + thickness_via + thickness_metal, material="tungsten", info={"mesh_order": 17}, ), # Metal 3 metal3=LayerLevel( layer=LAYER.Metal3drawing, thickness=thickness_metal, zmin=z_beol + 2 * thickness_via + thickness_metal, material="aluminum", info={"mesh_order": 18}, ), # Via 3 via3=LayerLevel( layer=LAYER.Via3drawing, thickness=thickness_via, zmin=z_beol + 2 * thickness_via + 2 * thickness_metal, material="tungsten", info={"mesh_order": 19}, ), # Metal 4 metal4=LayerLevel( layer=LAYER.Metal4drawing, thickness=thickness_metal, zmin=z_beol + 3 * thickness_via + 2 * thickness_metal, material="aluminum", info={"mesh_order": 20}, ), # Via 4 via4=LayerLevel( layer=LAYER.Via4drawing, thickness=thickness_via, zmin=z_beol + 3 * thickness_via + 3 * thickness_metal, material="tungsten", info={"mesh_order": 21}, ), # Metal 5 metal5=LayerLevel( layer=LAYER.Metal5drawing, thickness=thickness_metal, zmin=z_beol + 4 * thickness_via + 3 * thickness_metal, material="aluminum", info={"mesh_order": 22}, ), # MIM capacitor - dielectric 40nm (TISMIM) + top plate 150nm (TMIMTOP) # Sits on top of Metal5 mim=LayerLevel( layer=LAYER.MIMdrawing, thickness=0.04 + 0.15, zmin=z_m5_top, material="sio2", info={"mesh_order": 23}, ), # Vmim - MIM via (connects MIM top plate to TopMetal1) vmim=LayerLevel( layer=LAYER.Vmimdrawing, thickness=thickness_topvia1 - 0.19, zmin=z_m5_top + 0.19 + 0.01, material="tungsten", info={"mesh_order": 24}, ), # TopVia1 topvia1=LayerLevel( layer=LAYER.TopVia1drawing, thickness=thickness_topvia1, zmin=z_m5_top, material="tungsten", info={"mesh_order": 25}, ), # TopMetal1 topmetal1=LayerLevel( layer=LAYER.TopMetal1drawing, thickness=thickness_topmetal1, zmin=z_tv1_top, material="aluminum", info={"mesh_order": 26}, ), # TopVia2 topvia2=LayerLevel( layer=LAYER.TopVia2drawing, thickness=thickness_topvia2, zmin=z_tm1_top, material="tungsten", info={"mesh_order": 27}, ), # TopMetal2 topmetal2=LayerLevel( layer=LAYER.TopMetal2drawing, thickness=thickness_topmetal2, zmin=z_tv2_top, material="aluminum", info={"mesh_order": 28}, ), # ============ Passivation / pad ============ # Passivation (TPASS1=1500nm + TPASS2=400nm) passiv=LayerLevel( layer=LAYER.Passivdrawing, thickness=1.5 + 0.4, zmin=z_tm2_top, material="sin", info={"mesh_order": 29}, ), ) )
[docs] class TechIHP(BaseModel): """IHP PDK Technology parameters.""" # Grid and precision grid: float = 0.005 # 5nm grid precision: float = 1e-9 # Design rules - transistors nmos_min_width: float = 0.15 nmos_min_length: float = 0.13 pmos_min_width: float = 0.15 pmos_min_length: float = 0.13 # Design rules - contacts and vias cont_size: float = 0.16 cont_spacing: float = 0.18 cont_enc_active: float = 0.07 cont_enc_poly: float = 0.07 cont_enc_metal: float = 0.06 cont_b1: float = 0.2 cont_b1_nr: float = 4.0 via1_size: float = 0.26 via1_spacing: float = 0.36 via1_enc_metal: float = 0.06 # Design rules - metal metal1_width: float = 0.14 metal1_spacing: float = 0.14 metal2_width: float = 0.16 metal2_spacing: float = 0.16 metal3_width: float = 0.20 metal3_spacing: float = 0.20 metal4_width: float = 0.20 metal4_spacing: float = 0.20 metal5_width: float = 0.20 metal5_spacing: float = 0.20 topmetal1_width: float = 1.0 topmetal1_spacing: float = 1.0 topmetal2_width: float = 2.0 topmetal2_spacing: float = 2.0 # Design rules - resistors rsil_sheet_res: float = 7.0 # ohms/square rppd_sheet_res: float = 300.0 # ohms/square rhigh_sheet_res: float = 1350.0 # ohms/square # Design rules - capacitors mim_min_size: float = 0.5 mim_cap_density: float = 1.5 # fF/um^2 # Design rules - inductors inductor_min_width: float = 2.0 inductor_min_spacing: float = 2.1 inductor_min_diameter: float = 15.0 # Design rules - FET layout epsilon: float = 0.001 cont_gate_dist: float = 0.11 # Cnt_f gatpoly_activ_over: float = 0.18 # Gat_c gat_d: float = 0.07 # Gat_d psd_activ_over: float = 0.18 # pSD_c psd_gate_over_lv: float = 0.3 # pSD_i pSD_a: float = 0.31 psd_gate_over_hv: float = 0.4 # pSD_i1 nw_activ_over_lv: float = 0.31 # NW_c nw_activ_over_hv: float = 0.62 # NW_c1 tgo_activ: float = 0.27 # TGO_a tgo_gatpoly: float = 0.34 # TGO_c m1_over: float = 0.0 # M1_c m1_endcap: float = 0.05 # M1_c1 # Design rules - RF layout (vias) via1_size_rf: float = 0.19 # V1_a via1_spacing_narrow: float = 0.22 # V1_b via1_spacing_wide: float = 0.29 # V1_b1 via1_width_threshold: float = 1.52 # W threshold for via spacing switch via1_enc: float = 0.01 # V1_c via1_endcap: float = 0.05 # V1_c1 # Design rules - RF layout (rings and spacings) rf_gate_ring_width: float = 0.3 rf_guard_ring_width: float = 0.32 rf_guard_ring_m1_width: float = 0.32 rf_psd_ring_width: float = 0.38 rf_sd_metal_width_over: float = 0.14 # metWidth = cont_size + this rf_active_gate_dist_x: float = 0.13 # dgatx (cnt_rows <= 2) rf_active_gate_dist_x_wide: float = 0.17 # dgatx (cnt_rows > 2) rf_active_gate_dist_y: float = 0.235 # dgaty rf_gate_guard_dist: float = 0.36 # dguard rf_channel_dist_base: float = 0.38 # dc scaling base rf_channel_dist_step: float = 0.03 # dc scaling step rf_endpiece_base: float = 0.345 # ec scaling base rf_endpiece_step: float = 0.065 # ec scaling step rf_short_wide_adjust: float = 0.005 # dce rf_short_wide_l_threshold: float = 0.14 rf_short_wide_w_threshold: float = 1.0 # Design rules - RF layout (contacts and offsets) rf_sd_margin_x: float = 0.05 rf_sd_margin_y: float = 0.015 rf_sd_metal_adjust: float = 0.02 rf_sd_row_spacing: float = 0.13 rf_gate_cont_offset: float = 0.02 # u_cont = u + this rf_gate_cont_margin_single: float = 0.075 # u when cnt_rows == 1 rf_gate_cont_margin_multi: float = 0.36 # u when cnt_rows > 1 rf_guard_cont_offset_h: float = 0.08 rf_guard_cont_offset_v: float = 0.11 rf_psd_pmos_inset_x: float = 0.5 rf_psd_pmos_inset_y: float = 0.6 rf_tgo_nmos: float = 0.35 # ThickGateOx extension (NMOS HV) rf_tgo_pmos: float = 0.31 # ThickGateOx extension (PMOS HV) rf_nw_pmos_hv: float = 0.35 # NWell extension (PMOS HV) rf_nw_pmos_lv: float = 0.31 # NWell extension (PMOS LV) rf_gate_pin_half_width: float = 0.1 # --- Device sizing limits (from sg13g2_tech.json) --- # NMOS (LV) nmos_max_width: float = 10.0 nmos_max_length: float = 10.0 nmos_max_nf: int = 100 # PMOS (LV) pmos_max_width: float = 10.0 pmos_max_length: float = 10.0 pmos_max_nf: int = 100 # NMOS HV nmos_hv_min_width: float = 0.30 nmos_hv_max_width: float = 10.0 nmos_hv_min_length: float = 0.45 nmos_hv_max_length: float = 10.0 nmos_hv_max_nf: int = 100 # PMOS HV pmos_hv_min_width: float = 0.30 pmos_hv_max_width: float = 10.0 pmos_hv_min_length: float = 0.40 pmos_hv_max_length: float = 10.0 pmos_hv_max_nf: int = 100 # RF NMOS (LV) rfnmos_min_width: float = 0.15 rfnmos_max_width: float = 10.0 rfnmos_min_length: float = 0.13 rfnmos_max_length: float = 10.0 rfnmos_max_nf: int = 40 # RF PMOS (LV) rfpmos_min_width: float = 0.15 rfpmos_max_width: float = 10.0 rfpmos_min_length: float = 0.13 rfpmos_max_length: float = 10.0 rfpmos_max_nf: int = 40 # RF NMOS HV rfnmos_hv_min_width: float = 0.33 rfnmos_hv_max_width: float = 10.0 rfnmos_hv_min_length: float = 0.45 rfnmos_hv_max_length: float = 10.0 rfnmos_hv_max_nf: int = 40 # RF PMOS HV rfpmos_hv_min_width: float = 0.39 rfpmos_hv_max_width: float = 10.0 rfpmos_hv_min_length: float = 0.40 rfpmos_hv_max_length: float = 10.0 rfpmos_hv_max_nf: int = 40 # BJT NPN (npn13G2, npn13G2L, npn13G2V) - only Nx validated; # emitter dimensions are fixed-size in the JSON but variable in our cells. npn_min_nx: int = 1 npn_max_nx: int = 10 # BJT PNP (pnpMPA) pnp_min_length: float = 0.68 pnp_max_length: float = 1000.0 pnp_min_width: float = 0.30 pnp_max_width: float = 2.0 # Resistors rsil_min_width: float = 0.50 rsil_max_width: float = 1000.0 rsil_min_length: float = 0.50 rsil_max_length: float = 1000.0 rppd_min_width: float = 0.50 rppd_max_width: float = 1000.0 rppd_min_length: float = 0.50 rppd_max_length: float = 1000.0 rhigh_min_width: float = 0.50 rhigh_max_width: float = 1000.0 rhigh_min_length: float = 0.96 rhigh_max_length: float = 1000.0 # Capacitors cmim_min_size: float = 1.14 cmim_max_size: float = 1000.0 rfcmim_min_size: float = 7.0 rfcmim_max_size: float = 1000.0 # MIM capacitor model parameters (from sg13g2_tech.json) cmim_caspec: float = 1.5 # fF/um² — area-specific capacitance cmim_cpspec: float = 0.04 # fF/um — perimeter-specific capacitance cmim_lwd: float = 0.01 # um — line-width delta rfcmim_caspec: float = 1.5 # fF/um² rfcmim_cpspec: float = 0.04 # fF/um rfcmim_lwd: float = 0.01 # um # Taps ptap1_min_size: float = 0.78 ptap1_max_size: float = 10_000_000.0 ntap1_min_size: float = 0.78 ntap1_max_size: float = 10_000.0 # Antennas dantenna_min_width: float = 0.48 dantenna_max_width: float = 1000.0 dantenna_min_length: float = 0.48 dantenna_max_length: float = 1000.0 dantenna_dov: float = 0.02 dpantenna_min_width: float = 0.48 dpantenna_max_width: float = 1000.0 dpantenna_min_length: float = 0.48 dpantenna_max_length: float = 1000.0 dpantenna_dov: float = 0.02 # Sealring sealring_min_width: float = 150.0 sealring_max_width: float = 32000.0 sealring_min_height: float = 150.0 sealring_max_height: float = 25000.0
TECH = TechIHP() LAYER_STACK = get_layer_stack() ROUTING_STACK = get_routing_stack() LAYER_VIEWS = gf.technology.LayerViews(PATH.lyp_yaml) def CbCapCalc(calc: str, c: float, length: float, width: float, cell: str) -> float: """Calculate MIM capacitor parameters (all dimensions in um, capacitance in fF). Args: calc: "C" (capacitance from l/w), "l" (length from C/w), "w" (width from C/l), "lw" (square dimension from C). c: Capacitance in fF (used when calc != "C"). l: Length in um. w: Width in um. cell: Model name ("cmim" or "rfcmim"). """ from math import sqrt caspec = getattr(TECH, f"{cell}_caspec") # fF/um² cpspec = getattr(TECH, f"{cell}_cpspec") # fF/um lwd = getattr(TECH, f"{cell}_lwd") # um if calc == "C": leff = length + lwd weff = width + lwd return leff * weff * caspec + 2.0 * (leff + weff) * cpspec elif calc == "l": weff = width + lwd return (c - 2.0 * weff * cpspec) / (caspec * weff + 2.0 * cpspec) - lwd elif calc == "w": leff = length + lwd return (c - 2.0 * leff * cpspec) / (caspec * leff + 2.0 * cpspec) - lwd elif calc == "lw": return ( -2.0 * cpspec / caspec + sqrt(4.0 * cpspec**2 / caspec**2 + c / caspec) - lwd ) return 0.0 ############################ # Cross-sections functions ############################ cross_section = gf.cross_section.metal1 @xsection def metal_routing( width: float = 1, layer: typings.LayerSpec = "M3", radius: float | None = None, port_names: typings.IOPorts = port_names_electrical, port_types: typings.IOPorts = port_types_electrical, **kwargs: Any, ) -> CrossSection: """Return Metal Strip cross_section.""" radius = radius or width return cross_section( width=width, layer=layer, radius=radius, port_names=port_names, port_types=port_types, **kwargs, ) # Metal routing cross-sections metal1_routing = partial( metal_routing, layer=LAYER.Metal1drawing, width=TECH.metal1_width * 2, port_names=gf.cross_section.port_names_electrical, port_types=gf.cross_section.port_types_electrical, radius=None, ) metal2_routing = partial( metal_routing, layer=LAYER.Metal2drawing, width=TECH.metal2_width * 2, port_names=gf.cross_section.port_names_electrical, port_types=gf.cross_section.port_types_electrical, radius=None, ) metal3_routing = partial( metal_routing, layer=LAYER.Metal3drawing, width=TECH.metal3_width * 2, port_names=gf.cross_section.port_names_electrical, port_types=gf.cross_section.port_types_electrical, radius=None, ) topmetal1_routing = partial( metal_routing, layer=LAYER.TopMetal1drawing, width=TECH.topmetal1_width, port_names=gf.cross_section.port_names_electrical, port_types=gf.cross_section.port_types_electrical, radius=None, ) topmetal2_routing = partial( metal_routing, layer=LAYER.TopMetal2drawing, width=TECH.topmetal2_width, port_names=gf.cross_section.port_names_electrical, port_types=gf.cross_section.port_types_electrical, radius=None, ) strip = topmetal2_routing metal_routing = topmetal2_routing cross_sections = get_cross_sections(sys.modules[__name__]) ############################ # Routing functions ############################ route_bundle = partial(gf.routing.route_bundle, cross_section="strip") route_bundle_rib = partial( route_bundle, cross_section="rib", ) route_bundle_metal = partial( route_bundle, straight="straight_metal", bend="bend_metal", taper=None, cross_section="metal_routing", port_type="electrical", ) route_bundle_metal_corner = partial( route_bundle, straight="straight_metal", bend="wire_corner", taper=None, cross_section="metal_routing", port_type="electrical", ) route_astar = partial( add_bundle_astar, layers=["TOPMETAL2"], bend="bend_euler", straight="straight", grid_unit=500, spacing=3, ) route_astar_metal = partial( add_bundle_astar, layers=["TOPMETAL2"], bend="wire_corner", straight="straight_metal", grid_unit=500, spacing=15, ) routing_strategies = dict( route_bundle=route_bundle, route_bundle_rib=route_bundle_rib, route_bundle_metal=route_bundle_metal, route_bundle_metal_corner=route_bundle_metal_corner, route_astar=route_astar, route_astar_metal=route_astar_metal, ) # techParams from CNI layer for cells2 compatibility techParams = _CNITech.get("SG13_dev").getTechParams() if __name__ == "__main__": LAYER_VIEWS.to_lyp(PATH.lyp)