资讯动态

从零搭建MOSFET SPICE模型:行为源实现与仿真验证

发布时间:2026/10/8 7:04:40 来源:尧图企业网站定制
在SPICE里调用一个MOSFET通常只需要一行网表语句M1 D G S 0 NMOS W10U L1U。这行语句的背后SPICE到底用什么公式算出漏极电流大多数工程师并不关心也不清楚。但如果有一天你需要给一种全新器件写模型或者想彻底搞清楚为什么某个电路老是仿真不收敛你就必须钻到模型的内部去。这篇文章我来分享一次完整实操完全从零搭建一个能跑通MOSFET基本工作原理的SPICE模型。不依赖任何PDK不直接调用内置模型从物理原理出发把MOSFET的伏安特性方程用SPICE行为源Behavioral Source实现成子电路再用ngspice做DC扫描验证输出特性与转移特性。整个过程用免费工具就能完整复现适合模拟IC设计初学者、对器件建模感兴趣的工程师以及想给特殊器件做macro model的人参考。1. 为什么要自己写一个MOSFET SPICE模型1.1 模型不是黑盒子自研模型能做什么绝大多数电路设计师用MOSFET模型的方式就是在原理图上放一个nmos或者pmos符号然后改宽长比。至于SPICE里那几十个参数分别对应什么物理过程模型的电流方程长什么样内部如何切换工作区几乎没人去深挖。这本身没什么问题PDK厂商已经帮你把模型校准好了直接用是最高效的。但一旦遇到以下情况黑盒子式的使用方式就会卡壳你拿到一种新工艺或特殊器件有机半导体TFT、氧化镓、碳化硅等厂商没有提供现成的SPICE模型需要自己根据测试I-V曲线写一个紧凑模型你需要做功率MOSFET或IGBT的宏模型macro model用于系统级仿真必须在行为级复现器件的外特性电路仿真不收敛需要判断问题是出在模型本身的数值行为还是你的电路拓扑你在给学生讲器件物理需要一个“透明”的模型把三个工作区的电流方程完整展示出来。这些场景下自己动手搭一个模型不是重复造轮子而是把“用模型”变成“懂模型”。一旦亲手写过一次你再回头看BSIM3的几百行参数表心态会完全不一样——你知道那些参数只不过是在把更复杂的物理现象用更多的方程片段拼接起来而已。1.2 选对模型级别从Level 1开始最靠谱SPICE里的MOSFET模型按复杂度分了好几个档次。Level 1是最早的Shichman-Hodges模型1969年提出方程简单到一页纸能写完Level 2、Level 3逐步加入了沟道电场、迁移率退化、亚阈值导电等修正到了BSIM3、BSIM4那就是几百个参数的工业级模型专门对付深亚微米器件。从零实现的第一原则是选一个“足够简单但物理上自洽”的起点。Level 1就是MOSFET模型世界里的Hello World参数少核心就VTO、KP、LAMBDA三个方程直观而且完美覆盖了截止、线性、饱和这三个基本工作区。它对长沟道器件通常L大于2到3微米有不错的精度对短沟道器件虽然误差大但这不是问题——我们的目标是理解原理不是做65纳米工艺的电路签核。还有一点很实用Level 1是ngspice、LTspice内置模型的一个标准档位LEVEL1这意味着你写完自己的模型之后可以直接拿内置模型跑同一组仿真做对比验证。这种“对标”是建模工作中极重要的一环能帮你快速发现方程实现中的错误。1.3 工具与仿真环境准备本文所有示例都用ngspice演示因为它是开源免费的跨平台而且对行为源B源、子电路、DC扫描的支持非常完整。你可以用包管理器直接安装也可以从ngspice官网下载编译好的版本。Windows用户如果习惯图形界面用LTspice也能跑通绝大部分示例只是控制语句的写法稍有差异我在第7节会专门讲兼容性坑。另外建议装一个Python环境配合matplotlib用来做数据对比绘图。文章第6节会用一个微型求解器做交叉验证你会发现Python版本和SPICE版本的模型可以互相印证这对排查“是模型错了还是SPICE语法错了”特别有帮助。2. MOSFET工作原理写模型前必须想清楚的三件事2.1 三个工作区截止、线性、饱和MOSFET有四个端子栅极G、漏极D、源极S、衬底B。常规的电路符号里衬底通常画成一个箭头指向沟道表示这是一个N沟道器件还是P沟道器件。N沟道增强型MOSFET的工作原理可以用一句话概括栅极电压超过阈值电压VTH之后栅氧下方的半导体表面会形成反型层也就是沟道把源和漏导通起来。隔着栅氧看这个器件的行为很像一条水渠配了一个水闸栅极电压就是闸门开度决定水渠里能流过多少水漏源电压就是水渠出口的落差决定水流的速度。闸门开得不够水渠是干的一滴水也过不去这是截止区闸门打开了但出口落差很小水量主要由落差决定水流近似线性的关系这是线性区也叫三极管区落差继续加大水渠某个位置会被“吸干”——靠近漏端的沟道发生夹断水流达到上限再加落差流量也只有微弱增加这是饱和区。用数学语言描述就是VGS决定“有没有沟道”VDS决定“沟道里的电流形态”。Level 1模型把这三个区间的电流方程分别写出来用VGS与VTH的大小关系、VDS与VGS-VTH的大小关系来切换。2.2 阈值电压与体效应VTH不是常数阈值电压VTH是MOSFET最核心的参数它定义了“栅极电压要多高才能形成沟道”。但很多人忽略的一点是VTH并不是一个固定值它会随着源极和衬底之间的电压而变化。原因在于当衬底相对于源极反向偏置时NMOS的VSB大于0耗尽层变宽栅极需要更高的电压才能把半导体表面拉到反型状态。这个现象叫体效应数学上用一个平方根公式描述VTH VTO GAMMA * (√(PHI VSB) - √PHI)其中VTO是零偏置时的阈值电压也就是VSB0时的阈值GAMMA是体效应系数PHI是表面势的近似值大约等于2倍的费米势。从公式可以看到VSB越大VTH越高。实际电路里如果源极和衬底没有接在一起——比如差分对里源极公共点电位浮起来或者NMOS放在不同的阱里——忽略体效应会导致仿真电流明显偏大。所以在搭模型的时候要么把衬底端当作摆设直接和源极短接这在很多基础教程里这么干要么老老实实把体效应写进去。既然要做“从零实现”我建议直接把体效应做进去这样模型才能算一个完整四端器件。2.3 沟道长度调制为什么饱和区不是一条直线理想MOSFET在饱和区电流应该完全不变是一条水平线。但实测曲线永远带一点上翘。原因是当VDS增大漏端的耗尽区会向沟道方向扩张等效地缩短了沟道长度L。沟道变短电流自然变大。这个现象叫沟道长度调制。Level 1模型用一个小技巧处理它在饱和电流公式后面乘一个(1 LAMBDA * VDS)的因子。LAMBDA就是沟道长度调制系数典型值在0.005到0.1每伏之间。这个修正虽然简单但效果很好它给出了一个有限输出电阻等效输出电阻约为ro 1 / (LAMBDA * ID)。回到水渠的类比饱和区里加大源头水压会把水渠尽头冲蚀掉一截渠道变短过水能力略微增加。LAMBDA就是这个“冲蚀程度”的量化。实际工作中LAMBDA是提取工艺参数时必然要拟合的量因为模拟放大器增益、恒流源输出阻抗都直接和它相关。3. Level 1模型数学表达从物理到方程3.1 三种工作区电流方程把上面的物理图像翻译成数学就得到Level 1模型的三个区间的电流方程。记VOV VGS - VTH过驱动电压对NMOS在VDS ≥ 0的正常正向工作条件下截止区VGS ≤ VTHID 0。线性区VGS VTH且VDS VOVID KP * (W/L) * [(VGS - VTH) * VDS - 0.5 * VDS²]。饱和区VGS VTH且VDS ≥ VOVID 0.5 * KP * (W/L) * (VGS - VTH)² * (1 LAMBDA * VDS)。这里面有几个物理细节值得展开。线性区公式里的VDS²/2项来自沟道电荷沿沟道方向的积分栅极到沟道的有效电压在源端是VGS-VTH到了漏端变成VGS-VTH-VDS沟道内电场强度是沿沟道变化的所以电流不是简单的欧姆定律而是一条抛物线。饱和区公式里的0.5因子同样是积分的产物。在VDS等于VOV的边界点上两个公式算出的电流相等都等于0.5 * KP * (W/L) * VOV²这保证了电流曲线的连续性。3.2 参数表与物理意义做模型之前先把参数表建立起来。下面是我在这个练习中使用的参数也是后续所有仿真和手算的共同基准。参数物理含义典型值单位对电路的影响VTO零偏阈值电压0.7V决定导通起点KP工艺跨导参数KP μn * Cox200µA/V²决定电流能力LAMBDA沟道长度调制系数0.021/V决定输出电阻GAMMA体效应系数0.5V^0.5决定衬底偏置灵敏度PHI表面势近似0.7V体效应公式内部参数W/L宽长比10-用户设计变量注意KP里其实包含了两个东西载流子迁移率μn和单位面积栅氧电容Cox。如果工艺的栅氧厚度变了KP也随之改变。这个参数在实际提取时可以从饱和区的√ID对VGS曲线的斜率获得我记得在讲参数提取时有个口诀饱和区√ID对VGS是直线斜率等于√(0.5 * KP * W/L)截距就是VTH。后面验证转移特性时我们正好能看到这一点。3.3 连续性分析直接套公式为什么不收敛把三个区间的公式用IF语句强行拼起来看起来天经地义但SPICE的牛顿-拉夫逊迭代算法对连续性和导数有要求。电流本身在区间边界VDS VOV处是连续的两边都等于同一个值。但看看导数的表现线性区内dID/dVDS KP * (W/L) * (VOV - VDS)在VDS VOV处这个导数等于0。饱和区里dID/dVDS 0.5 * KP * (W/L) * VOV² * LAMBDA。当LAMBDA不为0的时候这个导数是正数。于是从线性区跨到饱和区的那一瞬间导数从0跳到一个正数。这个“导数不连续”在数学上叫折点对牛顿迭代非常不友好。牛顿法在做迭代时如果初始猜测落在边界附近会在两个区间之间来回震荡产生非收敛或收敛慢的问题。此外行为源里的IF语句本身是一个硬切换SPICE在计算雅可比矩阵时对这个切换点附近的处理也比较粗糙。好在实际使用中只要LAMBDA不大0.02这种水平带点串联电阻大多数DC扫描都能顺利收敛。但了解这个机理很重要——当你遇到“仿真在某一个具体的VDS值附近死活不收敛”时第一反应就应该是这里有一个导数跳变点。4. 在SPICE中用行为源从零搭模型4.1 行为源与子电路两者合起来就是一个“迷你MOSFET”SPICE的行为源B源是一种电压或电流受控源它的输出值可以由节点电压的任意表达式定义。语法非常简单Bxxx n n- I{表达式}定义电流源Bxxx n n- V{表达式}定义电压源。表达式中可以用V(node1,node2)取节点压差可以用IF、SQRT、ABS这些函数。子电路则是一组SPICE语句的封装用.SUBCKT开头.ENDS结尾外部通过X开头的实例来调用。子电路的好处是可以带参数比如宽长比W/L这样同一个模型就能对应不同尺寸的器件。我们把B源放在子电路里子电路四个引脚就是D、G、S、B一个“迷你MOSFET”就诞生了。4.2 基础NMOS子电路先把不带体效应的跑通先从最简单的版本开始。定义一个名为MY_NMOS的子电路四个引脚D G S B参数包括W、L、VTO、KP、LAMBDA。核心就是那个B源用嵌套的IF语句实现三个工作区的切换。* 文件名: mynmos.inc .SUBCKT MY_NMOS D G S B PARAMS: W10U L1U VTO0.7 KP200U LAMBDA0.02 BID D S I { IF( V(G,S) VTO, 0, IF( V(D,S) (V(G,S)-VTO), KP*(W/L)*((V(G,S)-VTO)*V(D,S) - 0.5*V(D,S)^2), 0.5*KP*(W/L)*(V(G,S)-VTO)^2*(1LAMBDA*V(D,S)) )) } .ENDS MY_NMOS逐行解释一下。第一行的.SUBCKT声明了子电路名和引脚顺序调用时必须按D、G、S、B的顺序接节点。PARAMS关键字后面跟默认参数值调用时可以用W20U L2U这类写法覆盖。B源的第一层IF判断VGS是否小于等于VTO是则输出0进入截止区否则进入第二层判断比较VDS和VOV选线性区或者饱和区的公式。这里有个容易踩的坑SPICE对大小写不敏感写VTO、vto、Vto都能识别但表达式里的V(G,S)这种节点电压函数逗号前必须是正端。另外SPICE里小写u和大写U都是微米前缀1U等于1e-6但是如果你手误写成1u和10U混用也不会错因为W/L是比值只要分子分母单位一致即可。这个版本完全没用B端口调用时把B和S接到同一个节点就行比如都接地。4.3 加入体效应四端口模型才算完整不带体效应的模型在源衬短接时工作得很好但真实的电路里源极电位常常浮动这时就必须考虑VSB对阈值的影响。我推荐的做法不是把整个表达式揉成一个巨大的IF而是拆成两个B源一个专门计算阈值电压VTH输出到内部节点TH另一个根据VTH计算漏极电流。这样每个B源的表达式都短小清晰调试的时候能单独看VTH节点对不对。* 文件名: mynmos.inc续 .FUNC VSQRT(X) { IF(X 0, 0, SQRT(X)) } .SUBCKT MY_NMOSB D G S B PARAMS: W10U L1U VTO0.7 KP200U LAMBDA0.02 GAMMA0.5 PHI0.7 BTH TH 0 V { VTO GAMMA*(VSQRT(PHI V(S,B)) - VSQRT(PHI)) } BID D S I { IF( V(G,S) V(TH), 0, IF( V(D,S) (V(G,S)-V(TH)), KP*(W/L)*((V(G,S)-V(TH))*V(D,S) - 0.5*V(D,S)^2), 0.5*KP*(W/L)*(V(G,S)-V(TH))^2*(1LAMBDA*V(D,S)) )) } .ENDS MY_NMOSB这里有一个细节值得多说一句。当源衬电压VSB取负值也就是衬底相对源极正向偏置时PHIVSB可能变成负数直接开根号会出问题。所以我定义了一个VSQRT函数对负的自变量直接返回0。虽然Level 1模型在衬底正向偏置时物理上已经不太可靠但这个钳位能避免仿真器遇到NaN中断属于防御性编程。BTH源把阈值电压以电压量的形式放到内部节点TH上BID源里直接用V(TH)参与比较和计算。SPICE子电路内部节点是局部作用域你实例化多个MY_NMOSB也不会互相冲突这一点可以放心。4.4 搭建测试台用DC扫描验证伏安特性模型写好了下一步就是测它。最经典的测试方式是扫描VDS和VGS画出输出特性曲线族。测试台网表如下* 文件: test_mynmos.ngspice .include mynmos.inc VGS G 0 DC 2.0 VDS D 0 DC 0 X1 D G 0 0 MY_NMOS W10U L1U .DC VDS 0 5 0.02 VGS 1 4 0.5 .control run setplot dc1 plot -i(VDS) xlabel VDS(V) ylabel ID(A) .endc .end.DC语句的第一个扫描变量VDS是内层扫描从0到5V步进0.02V第二个扫描变量VGS是外层扫描从1V到4V步进0.5V所以会得到7条曲线。.control到.endc是ngspice的控制块run执行分析setplot dc1选中DC分析结果plot -i(VDS)画图。这里必须解释一下-i(VDS)的符号问题。I(VDS)是电压源内部从正端流向负端的电流但在测试电路里漏极电流是从节点D经MOSFET流到地和I(VDS)的方向正好相反所以I(VDS)是负的。画图的时候取负号才能得到正的ID曲线。这个符号问题几乎每个初学者都栽过我建议你在自己的工程文档里把它写死凡是直接用电压源测电流先想清楚电流方向和电压源的关系。跑完这个网表你应该能看到一组典型的MOSFET输出特性曲线每个VGS台阶对应一条从原点出发先近似直线然后弯曲进入饱和区的曲线饱和区略带正斜率。这组曲线就是我们模型的“身份证”它正确就说明Level 1方程实现没有大问题。5. 仿真验证曲线出来了才算数5.1 输出特性曲线手算与仿真对账仿真曲线出来之后最重要的一步是手算几个关键点和仿真结果对账。建模不是“画得像”就行每个数值都要经得起推敲。以VGS 2V为例VTH 0.7V所以过驱动电压VOV 1.3V。线性区和饱和区的分界点在VDS 1.3V。饱和电流为ID 0.5 * 200µA/V² * 10 * (1.3V)² * (1 0.02 * VDS)在VDS 1.3V处电流等于0.5 * 200µ * 10 * 1.69 1690µA也就是1.69mA。由于LAMBDA0.02VDS每增加1V电流大约增加2%。在VDS 5V时ID约等于1.69mA * (1 0.02 * 5) 1.86mA。再看VGS 1V的情况VOV 0.3V饱和电流只有0.5 * 200µ * 10 * 0.09 90µA。你会发现VGS从1V到2V电流从90µA跳到1.69mA差了将近19倍这正是饱和区电流和过驱动电压平方关系的结果。仿真跑出来的数值应该和这些手算值严格对应。如果对不上优先检查KP的单位、W/L的赋值、以及是否忘掉了0.5这个因子。5.2 转移特性与跨导验证方程的二阶关系输出特性验证的是VDS方向的物理转移特性验证的是VGS方向的物理。固定VDS 3V扫描VGS从0到5V。因为VDS足够大只要VGS超过VTH器件就工作在饱和区于是ID与(VGS-VTH)²成抛物线关系。* 文件: test_transfer.ngspice .include mynmos.inc VGS G 0 DC 0 VDS D 0 DC 3 X1 D G 0 0 MY_NMOS W10U L1U .DC VGS 0 5 0.01 .control run setplot dc1 plot -i(VDS) plot deriv(-i(VDS)) .endc .end转移特性曲线应该在VGS 0.7V以下保持为0跨过0.7V后呈现二次曲线上升。deriv(-i(VDS))就是跨导gm dID/dVGS。在饱和区理论值gm KP * (W/L) * (VGS - VTH)。比如VGS 2V时gm 200µ * 10 * 1.3 2.6mS你可以直接在deriv绘制的曲线上量一下应该非常接近这个值。这一节顺带展示了一个重要的参数提取方法把√ID对VGS画出来在饱和区它是一条直线斜率等于√(0.5 * KP * W/L)和横轴的交点就是VTH。如果你的模型没有bug这条直线会精确穿过(0.7, 0)这一点。5.3 与ngspice内置Level 1模型对比评估自己写的模型再怎么说也是“自嗨”真正的试金石是跟SPICE内置的Level 1模型对比。内置模型就是大家天天用的那个东西它经过了无数人的验证。我们把同样的参数喂给内置模型跑完全相同的扫描* 文件: test_builtin.ngspice M1 D G 0 0 NMOSL1 W10U L1U .MODEL NMOSL1 NMOS (LEVEL1 VTO0.7 KP200U LAMBDA0.02) VGS G 0 DC 2.0 VDS D 0 DC 0 .DC VDS 0 5 0.02 VGS 1 4 0.5 .control run setplot dc1 plot -i(VDS) .endc .end对比方法上建议用wrdata把两组数据存成CSV然后在Python里画在一起wrdata mynmos_out.csv -i(vds)import matplotlib.pyplot as plt import pandas as pd df_mine pd.read_csv(mynmos_out.csv, comment#) df_builtin pd.read_csv(builtin_out.csv, comment#) # 取 VGS2V 那一段数据绘制对比 plt.plot(df_mine.iloc[:, 0], df_mine.iloc[:, 1], labelMY_NMOS) plt.plot(df_builtin.iloc[:, 0], df_builtin.iloc[:, 1], --, labelBuilt-in Level 1) plt.legend() plt.show()在VDS ≥ 0的正向工作区两条曲线应该几乎完全重合误差控制在百分之零点几以内。我自己实测时差异主要来自内置模型内部对反型电荷的数值处理细节肉眼基本看不出区别。不过这里必须坦率指出我们模型的局限内置模型对VDS 0的反向工作区也有定义电流会平滑翻转方向而我们的IF版本在VDS为负时进入不了任何一个正确的物理描述会给出匪夷所思的结果。所以这个自研模型严格来说只适用于正向工作区做系统级模型或者教学演示完全够用但如果你要仿真同步整流这类存在反向导通的电路必须补齐反型区的方程。6. 用Python做交叉验证从方程到数值求解6.1 把Level 1方程翻译成Python函数SPICE里的仿真结果其实还可以用另一种方式验证直接写一段Python代码把相同的方程一字不差地实现一遍。这等于给模型提供了一个“独立参考实现”。好处是当SPICE仿真结果和你预期不符时你能用Python快速单点计算判断到底是方程写错了还是SPICE语法有问题。import numpy as np def ids_nmos(vgs, vds, vto0.7, kp200e-6, w10e-6, l1e-6, lam0.02, gamma0.0, phi0.7, vsb0.0): Level 1 NMOS 漏极电流单位安培。 if gamma 0: vth vto gamma * (np.sqrt(max(phi vsb, 0.0)) - np.sqrt(phi)) else: vth vto vov vgs - vth if vov 0.0: return 0.0 if vds vov: return kp * (w / l) * (vov * vds - 0.5 * vds * vds) return 0.5 * kp * (w / l) * vov * vov * (1.0 lam * vds)注意Python版的阈值电压计算里用了max(phi vsb, 0.0)这正是SPICE版里VSQRT函数做的那个钳位。保持两边逻辑一致对账才可信。调用一下ids_nmos(2.0, 3.0)理论上应该得到约1.73毫安VDS3V时饱和电流1.69mA乘以1.06的沟道长度调制因子。6.2 用牛顿-拉夫逊求DC工作点你到底在解什么SPICE最核心的DC工作点求解本质上是在解一组非线性方程组。拿最简单的电阻负载共源极放大器来看电路方程就一个节点方程MOSFET漏极电流等于电阻电流也就是ID(VGS, VDS) (VDD - VDS) / RD。这个方程只有一个未知数VDS一维零点问题用牛顿-拉夫逊迭代就能解。def solve_cs(vdd5.0, rd1e3, vg2.0, vds_init1.0, tol1e-12, **kw): 牛顿迭代求解共源极直流工作点返回 (VDS, ID)。 vds vds_init for _ in range(50): ids ids_nmos(vg, vds, **kw) f ids - (vdd - vds) / rd # KCL 残差 eps 1e-9 f_dot (ids_nmos(vg, vds eps, **kw) - ids) / eps 1.0 / rd vds_new vds - f / f_dot # 牛顿更新 if abs(vds_new - vds) tol: return vds_new, ids_nmos(vg, vds_new, **kw) vds vds_new raise RuntimeError(牛顿迭代未收敛)迭代过程可以这样理解先猜一个VDS算出此时KCL的残差f再估计f对VDS的斜率f_dot然后沿着斜率把残差压到零的方向迈一步重复直到变化量足够小。SPICE内部做的事情和这段Python几乎一样只不过它的未知量是从几个到几万个节点电压靠稀疏矩阵求解整个线性化系统。用上面的参数跑一下VDD5VRD1kΩVG2V手算结果是VDS约等于3.2VID约等于1.80mA。solve_cs()应该给出非常接近的值。这一步跑通说明你不仅会搭模型还真正理解了这个模型在电路里是如何被“求解”的。6.3 与SPICE仿真结果对账最后把这个共源极电路原样搬进ngspice用.op分析直接打印工作点* 文件: test_cs.ngspice .include mynmos.inc VDD VD 0 DC 5 RD VD VOUT 1K VG G 0 DC 2 X1 VOUT G 0 0 MY_NMOS W10U L1U .OP .control run print v(vout) .endc .end这个电路里VOUT就是漏极节点电压也就是我们上面解的VDS。仿真打印出的V(VOUT)应该和Python求解结果一致误差在微伏级别。两边的方程一模一样只是求解路径不同这个对账实际上是在验证SPICE行为源实现的正确性。7. 常见问题与排查技巧7.1 仿真不收敛的三大对策自写模型最常见的翻车现场就是不收敛。根据我的经验大概率是以下三个原因之一。第一个是IF切换点的导数跳变。前面第3.3节提过LAMBDA不为0时线性区和饱和区的边界处导数不连续牛顿迭代容易在边界附近抽搐。最有效的办法是给漏极串联一个很小的电阻人为引入一个寄生节点把电流求解的“地形”磨平一点。给子电路加一个参数化的串联电阻.SUBCKT MY_NMOSR D G S B PARAMS: W10U L1U VTO0.7 KP200U LAMBDA0.02 RDS1M RD D DINT {RDS} BID DINT S I { ... } .ENDS MY_NMOSR这里要特别提醒SPICE单位的坑1M在SPICE里是1毫欧姆不是1兆欧姆。SPICE规定标准后缀里M表示毫1e-3兆欧要用1MEG。无数工程师在这个地方写错过自查一下你的网表里有没有这种单位混淆。第二个原因是初始猜测离解太远。可以用.nodeset或者.ic给关键节点一个初始电压比如.nodeset V(d)2 V(g)2能显著提升收敛概率。第三个原因则是容差设置太苛刻或太宽松可以尝试.options reltol1e-4 abstol1e-12配合调整。7.2 曲线形状不对系统性排查表曲线能跑出来但形状不对这是另一个高频问题。我总结了一张排查表按现象速查现象可能原因检查项截止区电流不为0VGS与VTO比较方向写反D/S引脚接反检查IF第一层条件确认V(G,S)是栅源电压线性区斜率不对KP或W/L数量级错误VDS²项系数不是0.5手算一个VDS很小的工作点逐项核对饱和区电流异常上翘LAMBDA设置过大临时把LAMBDA设为0看曲线是否变平体效应完全没效果B端口没接GAMMA写成0PHI用了0测量V(TH)节点电压验证VSB变化是否抬升VTH有过冲或振铃IF切换导数跳变加RDS、增大步长细化扫描我自己的习惯是每次改完模型参数先在Python参考实现里手算三个点截止区一点VGS0.5V、线性区一点VGS2VVDS0.5V、饱和区一点VGS2VVDS3V。三个点全对才轮到SPICE出场。这样能快速隔离“数学错误”和“语法错误”。7.3 不同SPICE工具的兼容性差异如果你打算把这份模型搬到LTspice或者其他SPICE工具里有几个兼容性差异要留意。ngspice的控制块.control/.endc和plot命令是它自己的方言LTspice没有这套东西。LTspice里跑DC扫描直接在网表里写.dc VDS 0 5 0.02 VGS 1 4 0.5再看结果时用波形窗口手动加曲线就行。行为源的表达式函数在两家基本通用但函数名的大小写处理有细微差别建议在文件头部加注释写明“本模型语法兼容ngspice和LTspice行为源”。.FUNC函数在ngspice里是全局定义不能写在.SUBCKT块内部只能放在子电路外面或者单独的include文件里这一点很容易踩坑。另外子电路内部节点名是局部作用域但节点0地是全局的不要在子电路内部把0当作普通节点随便用。最后是参数覆盖语法。.SUBCKT...PARAMS:在ngspice和LTspice里都认但实例化时覆盖参数的写法是X1 D G S B MY_NMOS W20U L2U参数名和值之间用等号多个参数用空格分隔。我见过有人在这里用逗号直接报错。7.4 行为源表达式太长用分节点法提升可读性最后一个建议偏工程习惯。行为源表达式一旦超过三层嵌套IF可读性急剧下降括号配对错误也难找。我的做法是把中间物理量单独用B源算出来输出到内部节点再供最终电流源引用。体的阈值电压就是典型的中间量第4.3节已经演示了这个套路。类似的你还可以把VOV V(G,S) - V(TH)单独算成一个节点电压甚至把“是否截止”判断也做成一个逻辑节点。这样每个B源都是一句通俗易懂的表达式调试时可以直接plot v(th)看阈值电压对不对这才是自研模型真正的优势——模型内部的每一处细节你都能看到。做完整套练习之后我最大的感受是建模这事儿没什么神秘的就是把你脑子里的物理公式翻译成仿真器能懂的语言再做一次彻底的对账。你对手里模型的理解深度完全取决于你愿意花多少时间去拆开它、检查它、质疑它。下次当你的仿真又双叒叕不收敛时不妨先想想是不是模型本身在某个边界处给你挖了一个导数跳变的坑。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑