3. MOSFET亚阈值摆幅提取与亚阈值区模型校准
任务目标
从实测亚阈值区转移特性(Id-Vg)中提取亚阈值摆幅 SS(mV/dec),并调整 SPICE Level 1 模型的亚阈值参数,使仿真亚阈值斜率与测量值一致。
环境与依赖
- Python 3.8+,依赖
numpy、scipy、matplotlib - SPICE 仿真器:ngspice(命令行
ngspice可用) - 实测数据:本案例内置一段合成但物理合理的亚阈值 Id-Vg 数据(含噪声),用于演示提取与校准流程
提取与校准流程
完整实现
import numpy as np
from scipy import stats
import subprocess, re, os
# ---------- 1. 实测亚阈值 Id-Vg 数据(合成,含噪声) ----------
Vg = np.linspace(0.0, 1.2, 61)
SS_true = 85.0 # mV/dec
Vth = 0.45
Id = 1e-7 * 10**((Vg - Vth) / (SS_true / 1000.0))
Id = np.clip(Id, 1e-12, 1e-3)
rng = np.random.default_rng(42)
Id *= 10**(rng.normal(0, 0.03, Id.size)) # 3% 对数噪声
# ---------- 2. 提取 SS ----------
def extract_ss(Vg, Id, lo=1e-10, hi=1e-5):
mask = (Id > lo) & (Id < hi)
x, y = Vg[mask], np.log10(Id[mask])
slope, intercept, r, p, se = stats.linregress(x, y)
ss = 1000.0 / abs(slope) # mV/dec
return ss, slope, intercept, r**2
ss_meas, slope_m, icpt_m, r2_m = extract_ss(Vg, Id)
print(f"[Meas] SS = {ss_meas:.2f} mV/dec, R^2 = {r2_m:.4f}")
# ---------- 3. 生成 ngspice 网表并仿真 ----------
def run_ngspice(nsub, vto, vg_start=0.0, vg_stop=1.2, vg_step=0.02):
net = f"""* Level1 subthreshold calibration
M1 d g 0 0 nmos W=10u L=1u
Vd d 0 0.05
Vg g 0 0
.model nmos nmos level=1 vto={vto} kp=120e-6 nsub={nsub:.3e} gamma=0.5 phi=0.7
.dc Vg {vg_start} {vg_stop} {vg_step}
.print dc i(vd)
.end
"""
with open("cal.sp", "w") as f:
f.write(net)
out = subprocess.run(["ngspice", "-b", "cal.sp"],
capture_output=True, text=True).stdout
vgs, ids = [], []
for line in out.splitlines():
m = re.match(r"\s*(\d+)\s+([\d.eE+-]+)\s+([\d.eE+-]+)", line)
if m:
vgs.append(float(m.group(2)))
ids.append(abs(float(m.group(3))))
return np.array(vgs), np.array(ids)
# ---------- 4. 迭代校准 NSUB ----------
def calibrate(target_ss, vto=0.45, tol=1.0, max_iter=15):
nsub = 1e17
for i in range(max_iter):
vgs, ids = run_ngspice(nsub, vto)
if len(vgs) == 0:
raise RuntimeError("ngspice 未返回数据,请检查安装")
ss_sim, *_ = extract_ss(vgs, ids)
err = ss_sim - target_ss
print(f"iter {i:2d} NSUB={nsub:.3e} SS_sim={ss_sim:.2f} err={err:+.2f}")
if abs(err) < tol:
return nsub, ss_sim
# SS 随 NSUB 增大而增大,用比例步长调整
nsub *= (1.0 + 0.15 * np.sign(err))
return nsub, ss_sim
nsub_opt, ss_sim = calibrate(ss_meas)
print(f"\n[Result] NSUB = {nsub_opt:.3e} cm^-3")
print(f"[Result] SS_meas = {ss_meas:.2f} mV/dec, SS_sim = {ss_sim:.2f} mV/dec")
print(f"[Result] 偏差 = {abs(ss_sim - ss_meas):.2f} mV/dec")
运行方式
pip install numpy scipy
python calibrate_ss.py
验证方法与通过条件
- 脚本打印
[Meas] SS = ...,应接近 85 mV/dec(合成数据真值),R² 应大于 0.99,说明亚阈值区线性拟合有效。 - 迭代过程中
SS_sim逐步收敛到SS_meas,最终|SS_sim - SS_meas| < 1 mV/dec即判定校准通过。 - 若 ngspice 未安装或网表报错,脚本会抛出
ngspice 未返回数据,需先确认ngspice -b可正常运行。
关键点说明
- SS 提取只在
Id处于 1e-10 ~ 1e-5 A 的亚阈值区间做 log10 线性回归,避免进入强反型区导致斜率失真。 - Level 1 模型中亚阈值斜率主要由
NSUB控制(通过体效应系数 gamma 与表面势 phi 影响),VTO主要平移曲线不改变斜率,因此校准只调NSUB。 - 迭代采用符号比例步长(每次 ±15%),简单稳健;若需更快收敛可改用二分法或牛顿法。