PDK电阻模型从查表到SPICE仿真:方块电阻与温度电压系数避坑
简介:面向集成电路设计、版图与工艺工程师的TSMC MIM电容技术资料,围绕MIM电容性能评估与设计规则参考展开,适合模拟/RF电路设计及器件建模人员查阅。压缩包仅含1份PDF文件,约2.2MB,便于集中检索与离线阅读;目前已有1853人学习下载,具备一定参考热度。内容覆盖测量结果、Sheet Resistance Table、Film Parameters及RF电阻模型,详细列出Rp、Rint、Rgrain等参数,并给出P+ poly、N+ poly、P+/N+ diff等film类型的阻值差异与温度、电压系数;同时说明电阻计算公式及-40℃~125℃、-3.3~3.3V等有效范围。读者可据此理解MIM电容相关建模参数、核对设计规则,并辅助电路仿真与版图评估。
1. 一份标题写着 MIM 电容、正文却全是电阻模型的工艺附录
翻 PDK 的时候最容易碰到这种情况:文件名叫 MIM 电容介绍,点进去第 30 页开始讲的却是 RF 电阻模型——方块电阻表、Rp/Rint/Rgrain 三参数、温度电压二阶系数、等效电路图,一直写到第 34 页的接触电阻分解。这不是文档写错了,而是工艺厂把无源器件(电容、电阻、变容管)塞进同一份附录里,前面讲电容结构,后面顺手把薄膜电阻的建模数据一起给了。对做模拟前端、偏置网络、匹配网络的人来说,这份东西的价值恰恰在后半段:它告诉你版图上画一个 10µm×1µm 的多晶硅电阻,在 -40°C 到 125°C、-3.3V 到 3.3V 之间到底会漂多少。下面按“先读表、再写公式、最后落到仿真”的顺序把它拆一遍,顺带说说我踩过的坑。
2. 先读表再动手:四种膜的方块电阻与 Rp/Rint/Rgrain 三参数
这份附录给的不是一个“典型值”,而是一整套带容差、带温度电压修正的模型参数。它把薄膜分成四类:带硅化物的 P+ 多晶硅、带硅化物的 N+ 多晶硅、无硅化物的 N+ 扩散、无硅化物的 P+ 扩散,另外单列了无硅化物多晶硅的 Rp/Rint/Rgrain 三项。读表之前先搞清楚这三项各自对应版图上哪一段物理结构,否则后面套公式必然对不上号。
2.1 三参数分别对应版图上的哪一段
无硅化物多晶硅电阻的横截面是这样的:两端是硅化物(CoSi2)做接触,中间一段是多晶硅本体,本体上面盖着 RPO(电阻保护层)挡住硅化,防止整条都变成低阻。电流从左边硅化物进、穿过一小段界面、走过多晶硅本体、再穿过右边界面、从右边硅化物出。所以总电阻拆成四块:
- Rp:多晶硅本体贡献,按方块电阻算,随 L/W 变,单位 Ω/sq;
- Rint:硅化物与多晶硅之间的界面电阻,物理上是电流从二维接触面挤进一维沟道时的扩散电阻,量纲是 Ω·µm,所以要做 1/(W-dW) 的归一化;
- Rgrain:晶粒边界散射带来的附加电阻,跟宽度无关,是个固定串联项,单位 Ω;
- Rcon:接触孔电阻,文档写 Rcon = Rc/nc,nc 是接触孔个数,默认 2。
对应的等效式是 R = 2·Rcon + 2·Rend + Rp,而 Rend = Rint/(W-dW) + Rgrain。注意这里 Rint 和 Rgrain 被合并进了“端电阻”Rend,再乘 2(左右各一份)。很多人在建模时直接把 Rint 当成一个固定电阻加进去,忘了它要除以有效宽度,结果窄器件误差能到 30% 以上——这是最典型的翻车点。
带硅化物的多晶硅和扩散电阻就简单得多,只有 Rp 一项,R = R0·[1+VC1·dV+VC2·dV²]·[1+TC1·dT+TC2·dT²],其中 R0 = Rsh·(L-deltaL)/(W-deltaW)。这类电阻阻值低(个位数 Ω/sq),主要用在做小电阻、ESD 限流、电源走线上的寄生建模。
2.2 表里的 ± 与 W 分档:精度到底落在哪
原始测量表给的容差不是装饰。P+ 多晶硅无硅化物是 311±65 Ω/sq,算下来 ±21%;N+ 是 292±66,±23%。这个量级意味着你按典型值算出来的偏置电流,在 worst case 下可能差两成。带硅化物的膜分了两档宽度:
| 膜类型 | 有效宽度 | Rsh (Ω/sq) | TC1 | TC2 | VC1 | VC2 | deltaW (µm) |
|---|---|---|---|---|---|---|---|
| P+ poly 带硅化物 | W≥2.0 | 7.8±2.5 | 2.88e-3 | 5.01e-7 | -7.89e-4 | 6.19e-3 | -0.051 |
| P+ poly 带硅化物 | 0.18≤W<2.0 | 6.7±3.5 | 2.88e-3 | 5.01e-7 | -7.89e-4 | 6.19e-3 | -0.051 |
| N+ poly 带硅化物 | W≥2.0 | 7.4±2.0 | 2.92e-3 | 2.66e-7 | 1.35e-3 | 7.15e-3 | -0.065 |
| N+ poly 带硅化物 | 0.18≤W<2.0 | 5.7±4.2 | 2.92e-3 | 2.66e-7 | 1.35e-3 | 7.15e-3 | -0.065 |
| N+ diff 无硅化物 | — | 59±5.8 | 1.47e-3 | 8.32e-7 | 7.55e-4 | 1.97e-4 | — |
| P+ diff 无硅化物 | — | 133±19 | 1.43e-3 | 7.82e-7 | -1.19e-3 | -1.80e-4 | — |
这张表里藏了两个必须注意的点。第一,W 跨过 2.0µm 时 Rsh 会跳变(7.8→6.7、7.4→5.7),而且窄档的容差更大(±3.5、±4.2),原因是窄线条的侧壁耗尽和微负载效应更明显。第二,带硅化物膜的 deltaW 是负值(-0.051、-0.065),无硅化物多晶硅的 deltaW 是正值(0.06061、0.08333)。符号方向相反,抄错一位,有效宽度就从“加宽”变成“收窄”,5µm 宽的器件能差出 2%。
无硅化物那组的独立参数表是这样:
| 参数 | P+ poly 无硅化物 | N+ poly 无硅化物 | 单位 |
|---|---|---|---|
| Rp | 311±65 | 292±66 | Ω/sq |
| Rint | 66.98 | 14.84 | Ω·µm |
| Rgrain | 39.39 | 37.22 | Ω |
| deltaL | 0.04696 | 0.07616 | µm |
| deltaW | 0.06061 | 0.08333 | µm |
P+ 的 Rint 是 N+ 的四倍多,这不是笔误,是硼在界面处的偏析行为跟磷不一样,接触势垒更高。做对称负载的电流镜时如果一边用 P 一边用 N,两边的端电阻差几十欧,匹配会直接崩掉。
2.3 把表落成一份可查询的参数文件
抄表不如建表。我一般会把这几页做成一个 Python 字典,字段名跟文档一一对应,后面算 R0、生成网表都从这里取,避免手抄出错。
# res_params.py # 键名规则: 膜类型_是否带硅化物, 值全部照抄附录表格 RES = { "ppoly_sil_wide": dict(rsh=7.8, tol=2.5, tc1=2.88e-3, tc2=5.01e-7, vc1=-7.89e-4, vc2=6.19e-3, dw=-0.051, wmin=2.0), "ppoly_sil_narrow":dict(rsh=6.7, tol=3.5, tc1=2.88e-3, tc2=5.01e-7, vc1=-7.89e-4, vc2=6.19e-3, dw=-0.051, wmin=0.18), "nploy_sil_wide": dict(rsh=7.4, tol=2.0, tc1=2.92e-3, tc2=2.66e-7, vc1=1.35e-3, vc2=7.15e-3, dw=-0.065, wmin=2.0), "nploy_sil_narrow":dict(rsh=5.7, tol=4.2, tc1=2.92e-3, tc2=2.66e-7, vc1=1.35e-3, vc2=7.15e-3, dw=-0.065, wmin=0.18), "ndiff_nosil": dict(rsh=59.0, tol=5.8, tc1=1.47e-3, tc2=8.32e-7, vc1=7.55e-4, vc2=1.97e-4, dw=0.0, wmin=None), "pdiff_nosil": dict(rsh=133.0, tol=19.0, tc1=1.43e-3, tc2=7.82e-7, vc1=-1.19e-3, vc2=-1.80e-4, dw=0.0, wmin=None), } # 无硅化物多晶硅的分布参数, 单独一张表 DIST = { "ppoly_nosil": dict(rp=311.0, rint=66.98, rgrain=39.39, dl=0.04696, dw=0.06061), "nploy_nosil": dict(rp=292.0, rint=14.84, rgrain=37.22, dl=0.07616, dw=0.08333), } def pick(ftype, w): """按宽度自动选档, 这是最容易漏的一步""" if ftype.endswith("_sil_wide") or ftype.endswith("_sil_narrow"): base = ftype.rsplit("_", 1)[0] return RES[base + ("_sil_wide" if w >= 2.0 else "_sil_narrow")] return RES[ftype]pick这个函数解决的是 W 跨档问题:调用方只报膜类型和宽度,取参数时自动判断落在哪一档。实际项目里我见过有人把 1.5µm 宽的电阻按 7.8 Ω/sq 算,仿真出来的压降比实测小 14%,回头查了两天才发现是没换档。参数名里的wmin字段留着是为了做 DRC 前的合法性检查,宽度低于 0.18µm 就该报错而不是硬算。
3. 把公式拆成可执行代码:R0、delta 修正与二阶温度电压系数
表格只是离散的锚点,真正决定精度的是那几个公式怎么落地。文档给的两组公式形式不同:带硅化物的器件是 R0 × 两个归一化修正因子,无硅化物的器件是 2·Rcon + 2·Rend + Rp 的物理分解。分开写,不要硬凑成一个函数。
3.1 R0 = Rsh·(L-deltaL)/(W-deltaW) 的实际偏差
这个式子里 deltaL 和 deltaW 是光刻与刻蚀带来的有效尺寸偏移。L 是画出来的长度,减去 deltaL 才是真正导电的长度;W 是画出来的宽度,减去 deltaW 才是真正导电的宽度。带硅化物那两档只给了 deltaW,没给 deltaL——这意味着文档默认长方向的修正可以忽略,或者已经折算进 Rsh 里了。那就老老实实按 deltaL=0 处理。
def r0_sil(ftype, L_um, W_um, T=25.0, dV=0.0): p = pick(ftype, W_um) dl = 0.0 # 表中未给带硅化物膜的 deltaL r0 = p["rsh"] * (L_um - dl) / (W_um - p["dw"]) dT = T - 25.0 # 基准是 25°C, 不是 0°C return r0 * (1 + p["vc1"]*dV + p["vc2"]*dV**2) \ * (1 + p["tc1"]*dT + p["tc2"]*dT**2) def r0_nosil(ftype, L_um, W_um, T=25.0, dV=0.0, nc=2, rc=None): p = DIST[ftype] weff = W_um - p["dw"] # 有效宽度, 正值修正 rend = p["rint"] / weff + p["rgrain"] rp = p["rp"] * (L_um - p["dl"]) / weff rcon = 0.0 if rc is None else rc / nc # Rc 绝对值文档未给 return 2*rcon + 2*rend + rp拿 P+ 带硅化物、L=10µm、W=1µm 试一下:取窄档 Rsh=6.7,dw=-0.051,R0 = 6.7×10/(1+0.051) = 63.75 Ω。如果误用宽档 7.8,得到 74.2 Ω,差 16%。再叠加 125°C 的温漂:dT=100,温度因子 = 1 + 2.88e-3×100 + 5.01e-7×10000 = 1.293,整体从 63.75 变成 82.4 Ω,温度带来的正向漂移接近 30%。这个数字在做高精度偏置时必须留在预算里。
电压项更有意思。同一器件加 3.3V:1 + (-7.89e-4×3.3) + 6.19e-3×10.89 = 1.0648,电阻反而变大 6.5%。原因是 VC2 是个正的大系数,二次项在高偏压下压过了一次项。而 N+ 扩散的 VC2 只有 1.97e-4,几乎不随电压变。这一条在低压差线性稳压的反馈分压里影响不大,但在 RF 开关的偏置路上会直接影响插损。
3.2 二阶系数的量级:100°C 温漂到底几个百分点
把六个膜的 TC1/TC2 摊开看,TC1 都在 1.4e-3 到 2.9e-3 之间,也就是每升温 1°C 电阻涨 0.14%~0.29%。二阶项 TC2 在 1e-7 到 8e-7 量级,只有在 dT 超过 60°C 之后才开始有 0.1% 以上的贡献。所以在 25°C 到 85°C 的常规工业温区,一阶近似够用;要做车规或者 -40°C 冷启动,二阶必须带上。
def temp_sweep(ftype, L_um, W_um, ts=(-40, 25, 85, 125)): for t in ts: r = r0_nosil(ftype, L_um, W_um, T=t) if "nosil" in ftype \ else r0_sil(ftype, L_um, W_um, T=t) print(f"{ftype:18s} T={t:>4}°C R={r:9.3f} Ω")跑一遍temp_sweep("ppoly_nosil", 10, 1)会看到端电阻项 Rgrain 是固定值,不随温度变,只有 Rp 和 Rint 跟着 TC 走,所以整体温度系数比理论值低一点。这一点在写网表时要注意:不能把整个电阻乘一个温度因子,得按项分别算。
3.3 无硅化物电阻的分布模型与两处公式不一致
文档第 34 页给了两个版本的等效式,一处写 R = 2·Rc + 2·Rint + 2·Rgrain + Rp,另一处写 R = 2·Rcon + 2·Rend + Rp,且 Rcon = Rc/nc、Rend = Rint/(W-dW) + Rgrain。这两式不是等价写法:前者把 Rint 和 Rgrain 当成固定电阻直接串联,后者把它们并入端电阻并做了宽度归一化。
正确的物理理解是后者。Rint 是 Ω·µm 量纲,必须除以有效宽度才是欧姆;Rgrain 是固定欧姆值,跟宽度无关,所以它加在 Rint/Weff 后面而不是前面。前一种写法只在 W 恰好等于某个归一化宽度的特殊情况下才对得上。我一般按 Rend 版本建模,然后在注释里标一句“原文第 34 页两式并存,取分布模型”。
还有 Rc 的问题:文档只给了 Rcon = Rc/nc 和 nc 默认 2,没给 Rc 的绝对值。长电阻(L/W > 10)时 Rcon 占比不到 1%,忽略无妨;短粗电阻(L/W < 2)时 Rcon 能占到 5% 以上,这时候必须向工艺厂要 Rc,或者干脆在版图上把接触孔数量加到 4 个以上再用 nc 折算。凭空猜一个 Rc 塞进模型,是最隐蔽的误差来源。
4. 从参数到仿真:写一个能扫温度和偏压的电阻 subckt
参数算对了不等于仿真对。网表里的电阻要么是固定值(前仿),要么是行为级表达式(后仿/ corner 扫描),写法不同,踩的坑也不同。
4.1 用 Python 生成 subckt 骨架
手写几十个阻值的网表容易错位,我一般用脚本生成。下面这段按参数表输出 SPICE 子电路,带 L/W 参数化、带温度和端电压依赖。
TPL = """.subckt {name} n1 n2 l=10u w=1u .param dT = TEMP - 25 .param dV = V(n1,n2) .param weff = w - ({dw}) .param r0 = {rsh}*(l-{dl})/weff R1 n1 n2 R='r0*(1+{vc1}*dV+{vc2}*dV*dV)*(1+{tc1}*dT+{tc2}*dT*dT)' .ends {name} """ def gen_subckt(name, ftype, w_um): p = pick(ftype, w_um) return TPL.format(name=name, dw=p["dw"], rsh=p["rsh"], dl=0.0, vc1=p["vc1"], vc2=p["vc2"], tc1=p["tc1"], tc2=p["tc2"]) print(gen_subckt("rppoly_hr_1u", "ppoly_sil_narrow", 1.0))生成的weff已经带了符号:dw 是 -0.051,w - (-0.051)就是w + 0.051,展开后网表里是加号。这样写的好处是符号只在一个地方出现,改参数表就全改,不会出现“这个文件加、那个文件减”的不一致。dV = V(n1,n2)让电阻自己感知端电压,corner 扫描时不用改网表。
仿真器层面,这种写法要求支持表达式电阻(多数商用 SPICE 和 Spectre 都支持R='...'),并且TEMP是内置变量。如果仿真器不认TEMP,就退化成在脚本里对每个温度点生成一份固定值网表,扫五个温度点也就五次循环。
4.2 偏置边界:-3.3~3.3V 与 0.5mA/um 的物理含义
文档明确给了有效区间:带硅化物的多晶硅 -3.3V 到 3.3V,N+ 扩散 0 到 3.3V,P+ 扩散 -3.3V 到 0V,无硅化物多晶硅的偏置上限是 0.5mA/µm。这些不是随便写的:
- 扩散电阻的区间是单边的,因为 N+ 在 P 衬底上形成一个反偏 PN 结,加反向电压时只有结漏电,加正向电压会导通,模型立即失效;
- 无硅化物多晶硅用电流密度而不是电压,因为它的阻值高(几百 Ω/sq),同样的电压下电流差异很大,用电流密度约束更合理;
- 温度上限 125°C 对应的是模型提取时的测试范围,超出后二阶多项式外推没有物理依据。
实际仿真里我一般会在 subckt 外面套一层保护:if(abs(dV) > 3.3) ...,但 SPICE 语法里这么写不通用,所以更常见的做法是在测试台里加钳位二极管或者把扫描范围直接限定住。做 corner 时把电压扫到 ±4V 去看趋势可以,但结论不能当真。
4.3 结果校验:三点抽查法
模型跑通之后必须回算三个点,跟手算对得上才算数:25°C/0V、125°C/0V、25°C/3.3V。第一点验证 R0 和尺寸修正,第二点验证温度系数,第三点验证电压系数。三个点都对,中间插值基本不会出问题。
# 三点抽查: 与网表仿真结果逐点对比 for t, v in [(25, 0.0), (125, 0.0), (25, 3.3)]: calc = r0_sil("ppoly_sil_narrow", 10, 1.0, T=t, dV=v) print(f"T={t}°C V={v}V -> 手算 {calc:.3f} Ω")如果某一点偏差超过 2%,先怀疑三件事:单位(L/W 是 µm 还是 m)、符号(deltaW 正负)、基准温度(25 还是 0)。九成的对不上都是这三条之一。
5. 避坑与排查:五条把仿真做翻车的现场记录
5.1 现象:窄电阻仿出来比实测高 15%,原因在 Rsh 没换档
现象是 1.5µm 宽的多晶硅电阻,仿真值稳定偏高,换了几组 L 都一样。查下来是取参数时按 W≥2.0 那档拿了 7.8 Ω/sq,而实际应该用 6.7。解决办法是把选档逻辑封进pick()函数,调用方只传宽度,不允许直接引用 Rsh 常量。改完之后偏差回到 1% 以内。这类错误最阴的地方是它只在小尺寸上显形,大电阻上看不出来。
5.2 现象:宽器件电阻偏小,原因在 deltaW 符号抄反
某次换工艺,我把 deltaW 从 0.06061 改成 0.051,忘了带符号,结果 5µm 宽的电阻仿真值比实测小 2%。原因是带硅化物膜的 deltaW 是负值,减负数等于加,有效宽度变大、电阻变小;无硅化物膜是正值,减正数等于收窄、电阻变大。两种膜的符号方向完全相反。解决方式是在参数表里保留原始符号,公式统一写成W - dw,绝不在填表阶段就先取绝对值。
5.3 现象:室温下电阻凭空多 7%,原因在温度基准用错
有一版网表用dT = TEMP,跑 25°C 时 dT=25,温度因子算成 1+2.88e-3×25=1.072,电阻凭空高 7.2%。文档写得很清楚 dT = T - Tnominal,Tnominal = 25°C。这个错误在 25°C 下最明显,在 0°C 时反而碰巧接近正确,所以做低温 corner 的人常常发现不了。解决办法是把dT = TEMP - 25写死在 subckt 模板里,任何人改不了。
5.4 现象:P+ 扩散加正偏压后仿真不收敛,原因在越界外推
现象是给 P+ 扩散电阻加 +1V,仿真报收敛失败或者算出负阻。查文档:P+ 扩散的有效偏置区间是 -3.3V 到 0V,加正压等于让衬底结正偏,模型里的多项式已经没有意义。同理 N+ 扩散只能加 0 到 3.3V。解决办法是在测试台里用理想二极管把偏置钳在合法区间,或者干脆分两段扫描,中间断开。
5.5 现象:短粗电阻误差 20%,原因在端电阻项漏了 1/W
现象是 L=1µm、W=5µm 的电阻,仿真值比实测低两成。原因有两个叠加:一是端电阻 Rend 里的 Rint 项必须除以有效宽度,宽器件这一项变小,但 Rgrain 是固定的,占比反而上升;二是接触电阻 Rcon 没算进去,短电阻里它能占 5% 以上。解决办法是按R = 2·Rc/nc + 2·(Rint/Weff + Rgrain) + Rp完整写,Rc 向工艺厂确认,nc 按版图实际孔数填,别用默认的 2 蒙混。
6. 进阶:把这几页做成自己的器件卡片
文档给的是通用参数,落到具体项目还得再压一层。我的习惯是给每个用到的电阻建一张“器件卡片”,把几何尺寸、选档结果、三个抽查点的值、以及允许的偏置范围写在一张表里,和网表放在同一个目录。这样下次别人接手,不用回头翻 PDF 第 30 页。
| 卡片字段 | 示例值 | 来源 |
|---|---|---|
| 器件名 | rppoly_hr_1u | 自定义 |
| 膜类型/选档 | P+ poly 带硅化物,窄档 | W=1.0 < 2.0 |
| L / W | 10 / 1.0 µm | 版图 |
| R0 @25°C | 63.75 Ω | Rsh·(L-dL)/(W-dW) |
| R @125°C | 82.4 Ω | 温度因子 1.293 |
| R @3.3V | 67.9 Ω | 电压因子 1.065 |
| 允许偏置 | ±3.3V | 文档 |
| 容差 | ±52%(窄档 ±3.5/6.7) | 表格 |
窄档的容差达到 ±52%,这个数字第一次看到会吓一跳,但它就是工艺给出的真实分布。做匹配对的时候不能只看典型值,得把蒙特卡洛的 sigma 也带进去:两个同尺寸电阻的失配大致按容差的 1/√2 估,再叠加版图上的梯度方向,才是能拿去流片的结论。
最后一个技巧:把温度扫描写成一行命令,每次改完网表跑一遍,把 -40/25/85/125 四个点的值打到日志里。
# 批量扫四个温度点, 结果直接落到 csv for t in -40 25 85 125; do echo "T=$t" spectre -f nutmeg -temp $t rload.scs 2>&1 | grep -i "r_top" done > sweep.csv不要小看这一行。我曾经因为改了一个 deltaW 的符号,只跑了 25°C 单点,觉得对上了就交了,结果 125°C 下偏了 18%,返工重做了整版偏置。从那以后我每次改电阻参数,都强制把 -40°C 和 125°C 两个端点跑一遍,哪怕只是改个注释。希望帮到你。
本文还有配套的精品资源,点击获取