ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

GM-ID设计方法:从Cadence仿真到Matlab查表的两级运放实践

GM-ID设计方法:从Cadence仿真到Matlab查表的两级运放实践 1. 从一条曲线说起GM-ID 方法到底在解决什么问题做模拟电路设计的人尤其是做放大器、跨导级、偏置电路这一块的几乎绕不开一个核心问题我到底该把 MOS 管偏置在哪个工作点这个问题听起来简单但实际工程里它决定了增益、带宽、功耗、线性度、噪声这一整串指标的走向。很多新手习惯的做法是打开仿真器扫一遍 Vgs看 Id 曲线然后凭感觉挑一个看起来比较顺眼的点。这种做法在课程设计里能过但一旦进入真实项目尤其是需要跨工艺角、跨温度验证的场合就会暴露出巨大的问题——你根本说不清楚为什么选这个点也说不清楚换一个工艺之后这个点还成不成立。GM-ID 方法也叫 gm/ID 设计方法学就是用来把这件事从凭感觉变成有依据的一套系统化流程。它的核心思想非常朴素不去直接看 Vgs 或 Vov而是看跨导 gm 与漏电流 Id 的比值。这个比值有一个非常好的性质——它在很大程度上与晶体管的尺寸无关主要由工艺和沟道长度决定。换句话说gm/ID 是一个归一化的设计变量它把器件多大和偏置多深这两件事解耦了。为什么这件事重要因为在传统设计流程里你一旦改了 W整个偏置点就跟着变你得重新扫一遍曲线重新调一遍电流镜来回迭代。而用 gm/ID 作为入口你可以先确定我要工作在中等反型还是强反型再根据这个选择去查表得到对应的 Id/W、gm/gds、Cgg 等参数最后才去算 W。整个流程是单向的、可复现的而不是来回试凑的。我个人的体会是GM-ID 方法最大的价值不在于它更准而在于它把设计意图显式化了。当你跟别人讨论一个放大器时你说我把它偏在 gm/ID15对方立刻就知道你大概在中等反型区线性度和功耗的折中大概是什么水平。这种沟通效率是看 Vgs 曲线永远给不了的。那这套方法落地需要什么需要一张以 L 为参数、以 gm/ID 为横轴的参数表。这张表从哪来从工艺库的模型里仿真出来。怎么仿真在 Cadence 里搭一个简单的测试台扫一遍导出数据然后用 Matlab 做后处理和可视化。这就是这篇博文要完整走一遍的流程。2. 测试台搭建为什么是二极管接法而不是固定 Vgs2.1 二极管接法测试台的结构与理由在 Cadence 里做 GM-ID 数据提取最经典的测试台就是二极管接法diode-connected把 MOS 管的栅极和漏极短接然后在漏极加一个电流源或电压源扫描这个源记录 Id 和 gm。为什么用二极管接法因为在这种接法下晶体管自动处于饱和区。你可以这样理解栅漏短接意味着 Vds Vgs而饱和区的条件是 Vds ≥ Vgs - Vth代入 Vds Vgs得到 Vgs ≥ Vgs - Vth也就是 Vth ≥ 0这永远成立。所以二极管接法的管子天然饱和你不需要去操心它到底在不在饱和区这个问题。相比之下如果你用固定 Vgs、扫 Vds 的方式你得手动保证 Vds 足够大而且随着 Vgs 变化饱和边界也在变测试台会变得很啰嗦。二极管接法把这个麻烦直接消掉了。具体搭法是这样的选一个 NMOS栅极和漏极接在一起源极接地。在漏极接一个 DC 电压源 Vd然后对这个 Vd 做 DC 扫描。仿真器会自己解出对应的 Id。同时在 ADE 的 output 里设置表达式把 gm、gds、cgg、vth、vdsat 这些参数都存下来。注意这里扫的是 Vd但因为栅漏短接Vd 实际上就等于 Vgs。所以扫描范围要覆盖从亚阈值到强反型的整个区间一般从 0 扫到 VDD 就够了。2.2 扫描范围与步长的取舍扫描范围好定步长是个容易被忽略的细节。我见过有人用 0.5V 的步长扫结果曲线在亚阈值区几乎是一条直线完全看不出指数特性。也有人用 1mV 的步长仿真跑了两个小时数据文件几百兆后处理的时候 Matlab 直接卡死。我的经验是用分段扫描或者用对数步长。亚阈值区大概 0 到 Vth 附近用细步长比如 2mV 到 5mV强反型区Vth 以上用粗步长比如 20mV 到 50mV。如果你嫌分段麻烦直接用 10mV 均匀步长也能接受对于大多数 0.18um 到 65nm 的工艺这个精度足够画出平滑的 gm/ID 曲线了。还有一个细节扫描点数不要超过 2000 个。Cadence 的 DC 扫描在点数过多时收敛会变慢而且导出的 CSV 文件在 Matlab 里读起来也慢。1000 到 1500 个点是比较舒服的区间。2.3 需要保存哪些参数这是整个流程里最关键的一步因为你后面 Matlab 能画出什么图完全取决于你现在存了什么。我建议至少存以下几项参数名含义用途Id漏电流计算 gm/ID 的分子gm跨导计算 gm/ID、gm/gdsgds输出电导计算本征增益cgg栅极总电容计算 fT、电容密度vth阈值电压判断反型程度vdsat饱和电压判断余量cgd栅漏电容评估 Miller 效应在 ADE 里这些参数可以通过dc分析后的annotate或者直接在 output setup 里用表达式提取。不同工艺库的表达式写法略有差异但大体上gm对应IS(M0:gm)或OP(M0 gm)这类写法具体要看你用的 PDK 文档。提示如果你用的是 spectre可以在 model 里加save M0:all来保存所有小信号参数省得一个个写表达式。但这样导出的数据列会很多Matlab 里要按列名索引稍微麻烦一点。3. Cadence 数据导出CSV 不是终点格式才是坑3.1 从 ADE 导出数据的几种方式Cadence 导出仿真数据常见的有三条路第一条是ADE 自带的 Export在 results 菜单里选 Export然后选 CSV 或 PSF。这条路最直接但导出的 CSV 有时候列名是乱的尤其是当你用了表达式的时候列名会变成expr1、expr2这种后处理时对不上号。第二条是用 OCEAN 脚本写一段脚本把仿真跑完然后用ocnPrint把数据打到文件里。这条路适合批量跑多个 L 值的情况因为你可以用循环把不同 L 的数据一次性导出来文件名带上 L 的标记后面 Matlab 读的时候按文件名区分。第三条是直接读 PSF 文件用 Cadence 自带的psf工具或者第三方库比如 Python 的psf-utils解析。这条路最灵活但门槛也最高适合已经有一套自动化流程的人。我个人的建议是如果你只跑一两个 L用第一条如果你要跑五六个 L 做完整参数表用第二条。OCEAN 脚本看起来吓人但其实模板很固定改几个变量就能复用。3.2 CSV 列名混乱的根因与处理很多人导出 CSV 之后打开一看第一行是Vd Id gm gds ...看起来没问题。但用 Matlab 的readmatrix或csvread读进去之后发现第一列是 Vd第二列是 Id第三列开始全是 NaN。为什么因为 Cadence 导出的 CSV 有时候会在数值列里混入单位比如1.234e-5A这种Matlab 默认的数值解析器读不了。处理办法有两个一是在导出时勾选不包含单位二是在 Matlab 里用readtable配合VariableNamingRule和自定义的ImportOptions来跳过单位。我一般用后者因为前者有时候在 ADE 里找不到那个选项。opts detectImportOptions(gm_id_data.csv); opts.VariableNamingRule preserve; opts setvartype(opts, double); data readtable(gm_id_data.csv, opts);如果列名里有空格或特殊字符readtable会自动改成下划线这时候你要用data.Properties.VariableNames看一下实际列名别硬编码。3.3 多 L 数据的组织方式做 GM-ID 参数表你肯定要跑多个沟道长度。比如 0.18um 工艺你可能要跑 L0.18、0.24、0.36、0.5、0.8、1.2 这几个值。每个 L 跑一次 DC 扫描导出一个 CSV。组织方式我推荐文件名带 L 标记比如nmos_L018.csv、nmos_L024.csv然后在 Matlab 里用一个循环读进来存到一个 cell array 或者 struct 里。这样后面画图的时候你可以用不同的颜色或线型区分不同 L一张图上看清楚 L 对 gm/ID 曲线的影响。注意不同 L 的扫描范围可能不一样。短沟道器件的 Vth 通常更低亚阈值区更窄所以扫描起点可以稍微高一点。但为了统一我一般都用同一个范围反正多扫一点不费事。4. Matlab 后处理从原始数据到可查表的设计曲线4.1 计算 gm/ID 和 gm/gds 的正确姿势数据读进来之后第一件事是算 gm/ID。这个简单gm_id gm ./ Id。但这里有个坑亚阈值区的 Id 非常小除以它得到的 gm/ID 会非常大有时候能到 30 以上。这不是错误亚阈值区的 gm/ID 理论上确实可以很高因为 gm 和 Id 都随指数变化比值趋于常数。但如果你直接画出来曲线在低电流端会翘得很厉害看起来不美观。处理办法是限制 gm/ID 的显示范围比如只画 5 到 25 这一段。这一段覆盖了从强反型到中等反型的绝大部分实用区间。亚阈值区虽然 gm/ID 高但 fT 低、匹配差实际设计里很少用那么深的亚阈值。gm/gds 就是本征增益这个直接算gm ./ gds。注意 gds 在亚阈值区也很小所以本征增益在低电流端会很高这符合直觉——电流越小输出电阻越大增益越高。4.2 用插值构建可复用的查找表画图只是第一步真正有用的是把曲线变成可以查的表。比如你想设计一个 gm/ID15 的放大器你需要知道在这个点上 Id/W 是多少、fT 是多少、Cgg 是多少。这些都可以通过插值得到。Matlab 里用interp1就行gm_id_target 15; id_w interp1(gm_id, Id_over_W, gm_id_target, pchip); ft interp1(gm_id, fT, gm_id_target, pchip);这里我用pchip而不是默认的linear因为 gm/ID 曲线是单调的pchip能保证插值结果也是单调的不会出现奇怪的振荡。提示插值之前记得把数据按 gm/ID 排序因为 Cadence 导出的数据是按 Vd 排序的而 gm/ID 随 Vd 是单调递减的所以其实已经有序了。但如果你做了任何筛选或合并最好还是sort一下。4.3 可视化一张图讲清楚设计折中GM-ID 方法最经典的可视化是把 gm/ID 作为横轴把 Id/W、fT、gm/gds、Cgg/W 等参数画在同一张图上。这样你一眼就能看出往左走gm/ID 增大功耗降低但速度变慢往右走gm/ID 减小速度快但功耗高。我一般会画四个子图左上Id/W vs gm/ID看电流密度右上fT vs gm/ID看速度左下gm/gds vs gm/ID看本征增益右下Cgg/W vs gm/ID看电容负担每个子图里用不同颜色的线表示不同的 L。这样一张图就能覆盖大部分设计决策。figure; subplot(2,2,1); for i 1:length(L_list) plot(gm_id{i}, id_w{i}, LineWidth, 1.5); hold on; end xlabel(gm/ID (1/V)); ylabel(Id/W (A/m)); legend(L_labels); grid on;这种图在项目评审的时候特别好用因为你可以指着图说我选这个点是因为它在 fT 和增益之间取得了平衡而且 Id/W 还在可接受范围内。5. 实操中容易踩的五个坑5.1 坑一忘了设置正确的体电位二极管接法的 NMOS源极接地体电位也要接地。如果你忘了接或者接了一个负电压Vth 会变整个曲线会平移。这个坑很隐蔽因为仿真不会报错只是结果不对。我建议在测试台里显式地把 B 端接地并且在 schematic 里标注清楚。5.2 坑二gm 的表达式的符号问题有些工艺库的 gm 表达式带符号比如gm可能是负的取决于电流方向定义。如果你直接拿它算 gm/ID会得到一个负值曲线就反了。处理办法是取绝对值gm_id abs(gm) ./ Id。但更稳妥的做法是先在 ADE 里看一眼 gm 的波形确认符号。5.3 坑三CSV 里的科学计数法Cadence 导出的 CSV有时候会用1.23E-05这种科学计数法Matlab 的readtable默认能读但如果你用的是csvread或dlmread就可能读成字符串。我建议统一用readtable并且显式指定NumHeaderLines和Delimiter。5.4 坑四不同 L 的数据点数量不一致如果你用 OCEAN 脚本跑多个 L每个 L 的扫描点数可能因为收敛问题而不一样。这时候你不能直接把数据矩阵拼起来得用 cell array 或者 struct 分别存。画图的时候也要用循环不能向量化。5.5 坑五Matlab 版本兼容性readtable在 R2013b 之后才有pchip更早。如果你用的是很老的 Matlab 版本可能得用csvread加手动解析。另外Matlab 的图形句柄系统在 R2014b 之后有大改如果你在网上抄了一段老代码set(gca, FontSize, 12)可能不生效得用gca.FontSize 12。6. 从 GM-ID 表到实际电路一个两级运放的设计实例6.1 设计指标与 gm/ID 的映射假设你要设计一个两级运放指标是DC 增益 60dB单位增益带宽 10MHz相位裕度 60 度负载电容 2pF功耗不超过 1mW。电源电压 1.8V。第一步是把系统指标翻译成 gm/ID 层面的约束。单位增益带宽 GBW gm1 / (2π * Cc)其中 Cc 是补偿电容。为了 60 度相位裕度Cc 一般取 0.2 到 0.3 倍的 CL这里取 0.5pF。那么 gm1 2π * 10MHz * 0.5pF ≈ 31.4uS。第二级需要提供足够的增益和摆幅gm2 一般取 2 到 3 倍的 gm1这里取 80uS。总功耗 1mW1.8V 电源总电流约 0.56mA。两级各分一半每级 0.28mA。现在你有了 gm 和 Id可以算 gm/ID 了第一级 gm1/Id1 31.4u / 0.28m ≈ 0.112也就是 gm/ID ≈ 11.2。第二级 gm2/Id2 80u / 0.28m ≈ 0.286也就是 gm/ID ≈ 28.6。6.2 查表确定 W 和 L有了 gm/ID你就可以去查之前生成的表了。第一级 gm/ID11.2属于强反型区速度较快但增益一般。第二级 gm/ID28.6属于中等反型甚至接近亚阈值增益高但速度慢——这正好符合两级运放的分工第一级管带宽第二级管增益。在表里找到对应的 Id/W假设第一级在 L0.18um 时 Id/W 5uA/um那么 W1 0.28m / 5u 56um。第二级在 L0.36um 时 Id/W 1uA/um那么 W2 0.28m / 1u 280um。这些数字不是最终值但已经给了你一个非常合理的起点。接下来你可以在 Cadence 里搭出电路跑 AC 仿真看实际增益和带宽然后微调 W 和 L。因为起点已经很接近目标微调的幅度通常很小迭代次数从原来的十几次降到两三次。6.3 验证与迭代搭好电路之后跑 AC 仿真看增益曲线和相位曲线。如果增益不够可以把第二级的 L 加大一点牺牲一点速度换增益。如果带宽不够可以把第一级的 gm/ID 调小一点也就是往强反型方向走增加 gm。这个过程里GM-ID 表的作用是给你方向。你知道往哪个方向调会得到什么效果而不是盲目地试。这是我个人觉得这套方法最值钱的地方。7. 把这套流程自动化的几个思路7.1 用 OCEAN 脚本批量跑 L前面提到过OCEAN 脚本可以一次性跑多个 L。基本结构是foreach(L list(0.18 0.24 0.36 0.5 0.8) desVar(L L) run() ocnPrint(?output sprintf(nmos_L%03d.csv L*1000) ?numberNotation scientific) )这段脚本会依次把 L 设成不同的值跑仿真然后导出 CSV。文件名里的L*1000是为了把 0.18 变成 018方便排序。7.2 Matlab 端的自动化读取与绘图Matlab 端可以用dir列出所有 CSV然后循环读files dir(nmos_L*.csv); for i 1:length(files) data readtable(files(i).name, opts); L_val str2double(files(i).name(6:8)) / 1000; % 计算 gm/ID 等 % 存到 struct end这样你加一个新的 L只需要在 OCEAN 脚本里加一行Matlab 端不用改。7.3 版本管理与可复现性最后说一个容易被忽略的点把测试台、OCEAN 脚本、Matlab 脚本都放到版本控制里。我用的是 git每次改工艺库或者改测试台都提交一次。这样半年后你回头看某个设计能准确知道当时用的是哪版数据、哪版脚本。这个习惯在团队协作里尤其重要因为别人可以复现你的结果。提示Cadence 的 schematic 和 ADE state 也可以导出成文本格式虽然可读性一般但至少能 diff。如果你用 git 管理建议把 ADE state 也一起提交。8. 一些个人体会这套 GM-ID 流程我从几年前开始用中间踩过不少坑也走过一些弯路。最大的体会是它不是一个更高级的方法而是一个更清晰的方法。你完全可以用传统的 Vgs 扫描做出一样好的电路但 GM-ID 让你在做的过程中知道自己在干什么也让别人能看懂你在干什么。另一个体会是数据质量比方法本身更重要。如果你的测试台搭错了体电位没接对或者 gm 表达式写错了那后面所有的曲线都是错的而且错得很隐蔽。所以我在每次跑完数据之后都会先画一张 Id vs Vgs 的图确认它看起来像一条正常的 MOS 曲线——亚阈值区指数上升强反型区线性上升。这一步花不了两分钟但能省掉后面几个小时的排查。最后Matlab 的可视化不要追求花哨。我见过有人把图画得跟艺术品一样但关键信息反而看不清。我的原则是一张图只讲一件事。Id/W 一张fT 一张增益一张不要挤在一起。颜色用默认的就行线宽 1.5 足够网格打开坐标轴标签写清楚单位。这样别人拿到你的图不用问你就能看懂。
返回列表