Files

272 lines
9.5 KiB
Python

# device_pcad_config.py
# Process-Step Oriented 2D Doping Configuration File for LDMOS
import os
import sys
import numpy as np
import math
# Add workspace directory to path for physics import
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
from physics.pcad_physics import get_implant_params, get_diffusion_coefficient
# All units in cm (1 um = 1e-4 cm)
um = 1e-4
from device_config import (
W_DEVICE, H_SI, N_SUB, SIM_NAME,
N_CON_PEAK, N_CON_DEPTH, N_CON_HDDIFF
)
# Define mask openings (Horizontal coordinate ranges, positive X-axis. Mirroring is handled dynamically)
masks = {
'pbl_mask': [(0.0 * um, 1.5 * um)], # Deep buried P-layer (mirrors to -2 ~ 2 um)
'ndrift_mask': [(1.5 * um, 5.5 * um)], # N-drift for supporting high voltage (mirrors to 3 ~ 9.5 um)
'nchannel_mask': [(0.5 * um, 5 * um)], # Light threshold adjustment channel layer (mirrors to 0.5 ~ 8.5 um)
}
# Define process steps for the 2D doping profile
process_steps = [
# Step 0: Substrate Wafer
{
'enabled': True,
'name': 'Substrate',
'method': 'Substrate',
'type': 'p',
'doping': N_SUB,
},
# Step 1: PBL (Deep P-buried Layer)
{
'enabled': True,
'name': 'PBL',
'method': 'Implant_and_Diffuse',
'type': 'p',
'dopant': 'Boron',
'energy': 800.0, # High energy for deep penetration
'dose': 1.0e13, # typical deep implant dose (cm^-2)
'mask': 'pbl_mask',
'temp': 1000.0, # High temp for deep diffusion drive-in 1150
'time': 20.0, # Long diffusion time 240
'lateral_ratio': 0.8,
},
# Step 2: N-Drift (Drift region supporting ~60V)
{
'enabled': True,
'name': 'NDrift',
'method': 'Implant_and_Diffuse',
'type': 'n',
'dopant': 'Phosphorus',
'energy': 300.0, # FOX 讓深度變深
'dose': 5.0e12, # typical drift implant dose (cm^-2)
'mask': 'ndrift_mask',
'temp': 1050.0,
'time': 120.0,
'lateral_ratio': 0.8,
},
# Step 3: N-Channel (Light threshold adjust / channel adjust)
{
'enabled': True,
'name': 'NChannel',
'method': 'Implant_and_Diffuse',
'type': 'n',
'dopant': 'Phosphorus',
'energy': 150.0,
'dose': 8.0e11, # light dose for channel adjustment
'mask': 'nchannel_mask',
'temp': 1000.0,
'time': 45.0,
'lateral_ratio': 0.6,
}
]
def resolve_steps():
"""
Resolves the physical process steps into analytical model parameters
(peak, vdiff, hdiff, x_ranges) for profile evaluation.
"""
resolved = []
# 1. Resolve substrate first
sub_step = [s for s in process_steps if s['method'] == 'Substrate'][0]
if sub_step.get('enabled', True):
resolved.append({
'name': sub_step.get('name', 'Substrate'),
'method': 'Substrate',
'type': sub_step['type'],
'doping': sub_step['doping']
})
# 2. Resolve implant/diffusion steps
for step in process_steps:
if step['method'] == 'Substrate' or not step.get('enabled', True):
continue
name = step.get('name')
type_ = step['type']
dopant = step['dopant']
# Look up mask coordinate ranges
mask_name = step['mask']
x_ranges = masks.get(mask_name, [])
# Get implant properties
energy = step.get('energy', 80.0)
dose = step.get('dose', 1.0e12)
Rp, dRp = get_implant_params(dopant, energy)
# Get thermal diffusion properties
temp = step.get('temp', 1000.0)
time_min = step.get('time', 30.0)
D = get_diffusion_coefficient(dopant, temp)
time_sec = time_min * 60.0
Dt = D * time_sec
# Standard deviation incorporating both implant straggle and thermal budget diffusion
vdiff = math.sqrt(2.0 * (dRp ** 2) + 4.0 * Dt)
# Horizontal standard deviation based on lateral ratio
lateral_ratio = step.get('lateral_ratio', 0.8)
hdiff = vdiff * lateral_ratio
# Calculate peak concentration matching the integrated dose for Gaussian profile:
peak = dose / (vdiff * math.sqrt(math.pi))
resolved.append({
'name': name,
'method': 'Implant_and_Diffuse',
'type': type_,
'peak': peak,
'vdiff': vdiff,
'hdiff': hdiff,
'Rp': Rp,
'x_ranges': x_ranges
})
return resolved
def apply_pcad_doping_2d(device, region="Silicon"):
"""
Parses the process steps list and registers the corresponding
2D analytical doping models in DEVSIM.
"""
import devsim
donor_terms = []
acceptor_terms = []
steps = resolve_steps()
# 1. Setup Substrate baseline first
sub_step = [s for s in steps if s['method'] == 'Substrate'][0]
sub_doping = sub_step['doping']
sub_type = sub_step['type']
devsim.node_model(device=device, region=region, name="nD_sub", equation=f"{sub_doping}")
if sub_type == 'n':
donor_terms.append("nD_sub")
else:
acceptor_terms.append("nD_sub")
# 2. Setup Fixed Contact N+ doping from device_config (does not change with PCAD steps)
expr_ncon_r = f"erfc(y / {N_CON_DEPTH}) * 0.5 * (erf((x - (8.5 * {um})) / {N_CON_HDDIFF}) - erf((x - (9.5 * {um})) / {N_CON_HDDIFF}))"
expr_ncon_l = f"erfc(y / {N_CON_DEPTH}) * 0.5 * (erf((x - (-9.5 * {um})) / {N_CON_HDDIFF}) - erf((x - (-8.5 * {um})) / {N_CON_HDDIFF}))"
devsim.node_model(device=device, region=region, name="nD_contact", equation=f"{N_CON_PEAK} * ({expr_ncon_r} + {expr_ncon_l})")
donor_terms.append("nD_contact")
# 3. Iterate and build expressions for Implant/Diffusion steps
for step in steps:
if step['method'] == 'Substrate':
continue
name = step['name']
type_ = step['type']
peak = step['peak']
x_ranges = step['x_ranges']
vdiff = step['vdiff']
hdiff = step['hdiff']
Rp = step['Rp']
# Construct analytical 2D Gaussian expression for each window range, including mirroring
expr_terms = []
for x1, x2 in x_ranges:
y_profile = f"(exp(-pow((y - {Rp}) / {vdiff}, 2)) + exp(-pow((y + {Rp}) / {vdiff}, 2)))"
# Right side (positive X)
expr_terms.append(f"{y_profile} * 0.5 * (erf((x - ({x1})) / {hdiff}) - erf((x - ({x2})) / {hdiff}))")
# Left side (negative X, mirrored)
expr_terms.append(f"{y_profile} * 0.5 * (erf((x - ({-x2})) / {hdiff}) - erf((x - ({-x1})) / {hdiff}))")
# Combine terms and multiply by peak concentration
combined_expr = f"{peak} * (" + " + ".join(expr_terms) + ")"
model_name = f"nA_{name}" if type_ == 'p' else f"nD_{name}"
devsim.node_model(device=device, region=region, name=model_name, equation=combined_expr)
if type_ == 'n':
donor_terms.append(model_name)
else:
acceptor_terms.append(model_name)
# 4. Combine separate models into global Donors and Acceptors fields in DEVSIM
donor_equation = " + ".join(donor_terms)
acceptor_equation = "1e10 + " + " + ".join(acceptor_terms)
devsim.node_model(device=device, region=region, name="Donors", equation=donor_equation)
devsim.node_model(device=device, region=region, name="Acceptors", equation=acceptor_equation)
devsim.node_model(device=device, region=region, name="NetDoping", equation="Donors - Acceptors")
devsim.node_model(device=device, region=region, name="LogNetDoping", equation="asinh(NetDoping / 2.0) / log(10.0)")
def get_pcad_doping_val_2d(x, y):
"""
Evaluates the 2D process-step doping profile analytically on numpy arrays x and y.
"""
erf_vec = np.vectorize(math.erf)
erfc_vec = np.vectorize(math.erfc)
donors = np.zeros_like(x, dtype=float)
acceptors = np.zeros_like(x, dtype=float)
steps = resolve_steps()
# 1. Setup Substrate baseline first
sub_step = [s for s in steps if s['method'] == 'Substrate'][0]
sub_doping = sub_step['doping']
sub_type = sub_step['type']
if sub_type == 'n':
donors += sub_doping
else:
acceptors += sub_doping
# 2. Add Fixed Contact N+ doping
expr_ncon_r = erfc_vec(y / N_CON_DEPTH) * 0.5 * (erf_vec((x - 8.5 * um) / N_CON_HDDIFF) - erf_vec((x - 9.5 * um) / N_CON_HDDIFF))
expr_ncon_l = erfc_vec(y / N_CON_DEPTH) * 0.5 * (erf_vec((x - (-9.5 * um)) / N_CON_HDDIFF) - erf_vec((x - (-8.5 * um)) / N_CON_HDDIFF))
donors += N_CON_PEAK * (expr_ncon_r + expr_ncon_l)
# 3. Iterate and build expressions for Implant/Diffusion steps
for step in steps:
if step['method'] == 'Substrate':
continue
type_ = step['type']
peak = step['peak']
x_ranges = step['x_ranges']
vdiff = step['vdiff']
hdiff = step['hdiff']
prof = np.zeros_like(x, dtype=float)
for x1, x2 in x_ranges:
# Right side
prof += erfc_vec(y / vdiff) * 0.5 * (erf_vec((x - x1) / hdiff) - erf_vec((x - x2) / hdiff))
# Left side (mirrored)
prof += erfc_vec(y / vdiff) * 0.5 * (erf_vec((x - (-x2)) / hdiff) - erf_vec((x - (-x1)) / hdiff))
if type_ == 'n':
donors += peak * prof
else:
acceptors += peak * prof
# Acceptor base offset 1e10
acceptors += 1e10
return donors - acceptors