1. 从一条曲线说起GM-ID 到底在解决什么问题做模拟电路设计的人对 GM-ID 方法论应该都不陌生。这套方法最早由 F. Silveira 等人在 1996 年提出核心思想是把 MOS 管的所有小信号参数——跨导 gm、输出电导 gds、特征频率 fT、本征增益 gm/gds——全部表达为归一化电流密度 ID/W 的函数而这个函数关系只跟工艺本身有关跟管子的具体尺寸无关。换句话说只要把某个工艺下 NMOS 和 PMOS 的 GM-ID 曲线族提取出来后续所有设计都可以在这套曲线上查表而不需要反复跑 DC 仿真去试参数。我刚开始接触这套方法的时候觉得它有点多此一举——Cadence 里直接跑个 DC 仿真看 gm、gds 的数值不就行了吗后来做低功耗运放的时候才明白传统方法的问题在于你调一个 W/Lgm 变了ro 也变了偏置电流也变了几个变量耦合在一起你根本不知道当前工作点离最优区域有多远。而 GM-ID 方法把设计自由度和工艺特性解耦了你只需要在归一化曲线上选一个工作点剩下的尺寸和电流自然就确定了。这套流程的完整链路是在 Cadence 里搭建测试台扫描管子的栅压和沟道长度导出 gm/ID、fT、gm/gds 等参数随 ID/(W/L) 变化的原始数据然后把数据导入 Matlab做插值、平滑、绘图生成可复用的设计曲线族最后在实际电路设计中根据增益、带宽、功耗指标反查曲线确定管子的工作点和尺寸。整条链路里Cadence 负责产生数据Matlab 负责加工和呈现数据中间的数据导出环节是最容易出问题的地方。这篇文章面向的是已经会用 Cadence 跑基本仿真、但还没系统跑过 GM-ID 流程的模拟 IC 设计者也适合做课程设计或毕业设计、需要快速建立工艺特性认知的同学。我会把整个流程拆开讲重点放在那些文档里不会写、但实际跑的时候一定会遇到的坑上。2. 测试台搭建为什么不能直接用单管 DC 仿真2.1 单管仿真台的结构设计很多人第一次跑 GM-ID直接在 Cadence 里放一个 NMOS栅极接电压源漏极接电压源跑 DC 扫描 VGS然后看 gm 和 ID。这样做能出数据但有几个致命问题。第一个问题是沟道长度调制效应。单管在饱和区VDS 固定的时候gds 是有限的但如果你只扫 VGS 不扫 VDS你得到的 gm/ID 曲线会包含 VDS 的影响而 GM-ID 方法论要求的是本征特性也就是 VDS 足够大、gds 影响可以忽略时的值。所以测试台必须包含 VDS 扫描或者至少把 VDS 固定在足够大的值上通常取 VDD 的 0.6~0.8 倍并且要验证在这个 VDS 下管子确实处于强饱和区。第二个问题是 W 和 L 的取值。GM-ID 曲线是归一化的理论上跟 W 无关但 L 必须作为参数扫描。因为不同 L 下管子的速度、增益、匹配特性都不一样。通常 L 取工艺最小 L 到 10 倍最小 L 之间的若干值比如 0.18um 工艺取 L 0.18, 0.24, 0.36, 0.5, 0.8, 1.2, 2.0, 4.0 um 这样一组。W 可以固定一个方便的值比如 10um因为归一化之后 W 会被除掉。第三个问题是体效应。如果你把源极接地、体极也接地那 VSB 0体效应为零。但实际电路中源极电位往往不等于体极电位所以测试台最好把体极单独接一个电压源扫描 VSB这样你还能顺便得到体效应系数随工作点变化的曲线。不过如果只是为了跑基本的 GM-ID 曲线VSB 0 也可以接受只是要在文档里注明。我常用的测试台结构是这样的NMOS 的源极和体极接地栅极接一个 DC 电压源 VG漏极接一个 DC 电压源 VD。跑 DC 仿真时扫描 VG 从 0 到 VDD同时用 parametric analysis 扫描 L 的一组值。仿真器会输出每个 L 下 ID、gm、gds、cgs、cgd 等参数随 VG 变化的曲线。2.2 仿真器设置里那些容易忽略的选项Cadence 的 DC 仿真默认设置里有几个选项会直接影响 GM-ID 数据的质量。首先是save选项。默认情况下仿真器只保存节点电压和少量电流gm、gds 这些派生参数需要显式保存。你需要在 ADE 的 Outputs 里添加gm、gds、cgs、cgd、ft等表达式或者直接在 netlist 里加.save语句。我一般会在 ADE 里用OP参数保存所有小信号参数这样跑完 DC 之后可以直接在 Results 里看到。其次是收敛精度。GM-ID 曲线在亚阈值区变化非常剧烈ID 从 pA 级到 uA 级跨越好几个数量级如果仿真器的相对误差设得太大比如默认的 1e-3亚阈值区的数据点会非常稀疏插值之后曲线会失真。我通常把reltol设到 1e-5abstol设到 1e-15vntol设到 1e-9。代价是仿真时间变长但 GM-ID 曲线只需要跑一次多花几分钟是值得的。第三是扫描点的密度。DC 扫描默认是线性步长但 ID 随 VG 是指数变化的线性步长在亚阈值区会漏掉很多细节。我一般用dec模式每十倍频程 100 个点或者直接用log扫描。如果仿真器支持还可以用adaptive模式让仿真器根据曲线曲率自动调整步长。提示跑完 DC 之后先别急着导出数据。在 Cadence 里把 gm/ID 对 ID/(W/L) 的曲线画出来看一眼如果曲线在亚阈值区出现明显的锯齿或跳变说明扫描点不够密或者收敛精度不够需要回去调仿真设置。2.3 参数扫描的组织方式L 的扫描有两种做法一种是在 ADE 里用 parametric analysis一次跑完所有 L另一种是每个 L 单独跑一次分别导出数据。前者效率高但导出的数据文件会包含所有 L 的曲线后续在 Matlab 里需要按 L 拆分。后者效率低但数据组织简单。我倾向于用 parametric analysis因为 Cadence 的ocean脚本可以一次性把所有 L 的数据导出成结构化的文本文件。具体做法是在 ADE 里设置好 parametric analysis跑完之后用ocnPrint命令把每条曲线的数据打印到文件里文件名里带上 L 的值。这样 Matlab 读进来的时候直接按文件名分组就行。如果你用的是spectre仿真器还可以用mdl或者psf格式导出数据精度比文本格式高但 Matlab 读取需要额外的解析工具。我一般用 CSV 格式虽然精度略低但通用性好Matlab 的readmatrix直接就能读。3. Cadence 数据导出从波形窗口到 CSV 的完整链路3.1 为什么直接复制波形数据不可靠很多教程教你直接在 Cadence 的波形窗口里选中曲线右键导出 CSV。这个方法能用但有几个隐患。第一波形窗口显示的是抽稀之后的数据。Cadence 为了显示流畅默认会对原始数据做降采样你看到的曲线可能只有几千个点而实际仿真可能产生了几十万个点。导出的 CSV 里就是这些降采样后的点亚阈值区的细节全丢了。第二波形窗口的导出功能对多曲线支持不好。如果你一次选中了 8 个 L 的 gm/ID 曲线导出的 CSV 里所有曲线混在一起没有明确的 L 标识后续在 Matlab 里拆分非常麻烦。第三波形窗口的导出格式依赖当前显示设置。如果你改了坐标轴范围或者开了对数坐标导出的数据可能被截断或者做了变换跟你预期的原始数据不一致。所以我的做法是不用波形窗口导出直接用ocean脚本或者ocnPrint命令从仿真结果数据库里打印原始数据。3.2 用 Ocean 脚本批量导出数据Ocean 是 Cadence 的脚本语言可以自动化仿真和数据处理。下面是我常用的一个导出脚本的骨架; 打开仿真结果数据库 openResults(simulation_results/psf) ; 获取所有 L 的列表 L_list list(0.18 0.24 0.36 0.5 0.8 1.2 2.0 4.0) ; 对每个 L 导出数据 foreach(L L_list ; 选择对应的曲线 selectResult(dc) ; 获取 VG 扫描变量 vg drGetWaveformXVec(VG) ; 获取 ID、gm、gds 等参数 id drGetWaveformYVec(ID) gm drGetWaveformYVec(gm) gds drGetWaveformYVec(gds) ; 计算归一化电流密度 id_norm id / (10.0 * L) ; W10um ; 打印到 CSV 文件 outfile sprintf(nil gm_id_L_%g.csv L) ocnPrint(?output outfile vg id id_norm gm gds ?numberNotation engineering ?numSpaces 1) )这个脚本的关键点在于drGetWaveformXVec和drGetWaveformYVec直接访问原始数据向量不做任何降采样ocnPrint的?numberNotation engineering选项保证数值以工程记数法输出避免科学记数法在 Matlab 里解析出错文件名里带上 L 的值方便后续分组。如果你不熟悉 Ocean 脚本也可以用 ADE 的Results - Print - Transient/DC菜单手动选择每条曲线打印到文件。虽然麻烦一点但效果一样。3.3 数据格式的坑单位、符号和缺失值Cadence 导出的 CSV 有几个常见的格式问题不处理的话 Matlab 读进来会出错。第一个是单位后缀。Cadence 的ocnPrint默认会在数值后面加单位比如1.234e-5会打印成12.34u。Matlab 的readmatrix不认识u、n、p这些后缀会直接报错。解决办法是在ocnPrint里加?numberNotation scientific强制用科学记数法输出不带单位后缀。第二个是符号问题。有些版本的 Cadence 在打印负数时会在数值和负号之间加空格比如- 1.234e-5Matlab 会把它解析成两个数。解决办法是在 Matlab 里用strrep把-替换成-或者用textscan自定义解析格式。第三个是缺失值。亚阈值区 ID 非常小可能低于仿真器的abstol导致某些点被标记为NaN或者直接缺失。Matlab 读进来之后这些点会变成NaN画图的时候曲线会断开。处理办法是在 Matlab 里用fillmissing做插值填充或者直接用isnan过滤掉这些点。我一般会在 Matlab 里写一个专门的解析函数处理这些格式问题function data parse_cadence_csv(filename) % 读取原始文本 fid fopen(filename, r); raw textscan(fid, %s, Delimiter, \n); fclose(fid); raw raw{1}; % 找到数据起始行跳过表头 start_idx find(~cellfun(isempty, regexp(raw, ^\s*[-]?\d, once)), 1); raw raw(start_idx:end); % 替换单位后缀和多余空格 raw regexprep(raw, \s, ); raw strrep(raw, - , -); % 解析数值 data zeros(length(raw), 5); for i 1:length(raw) vals sscanf(raw{i}, %f); if length(vals) 5 data(i, :) vals(1:5); end end % 过滤 NaN 和无效行 data data(~any(isnan(data), 2), :); end这个函数处理了单位后缀、负号空格、缺失值三个问题实测下来对大多数 Cadence 导出的 CSV 都能正常工作。4. Matlab 可视化从原始数据到可复用设计曲线4.1 数据清洗插值、平滑和异常点剔除Cadence 导出的原始数据直接画出来往往很难看。亚阈值区的点稀疏饱和区的点密集曲线在过渡区会有明显的折角。所以第一步是数据清洗。插值的目的是把非均匀采样的数据变成均匀采样方便后续计算和绘图。我一般用interp1做对数插值因为 ID 跨越多个数量级线性插值在亚阈值区会严重失真% 原始数据id_norm 和 gm_id % 先取对数再插值 log_id log10(id_norm); log_id_uniform linspace(min(log_id), max(log_id), 1000); gm_id_uniform interp1(log_id, gm_id, log_id_uniform, pchip); id_norm_uniform 10.^log_id_uniform;pchip是保形插值不会像spline那样在数据稀疏区产生过冲适合 GM-ID 这种单调性较强的曲线。平滑的目的是去掉仿真器数值噪声带来的毛刺。我一般用smoothdata的gaussian方法窗口大小取数据点数的 1% 左右。窗口太大会把曲线的真实特征也平滑掉太小则去噪效果不明显。异常点剔除主要是处理仿真不收敛导致的跳变点。判断方法是看相邻点的二阶差分如果某个点的二阶差分超过阈值的 10 倍就把它标记为异常点用邻近点的插值替换。注意数据清洗的顺序很重要。先剔除异常点再插值最后平滑。如果先平滑再插值异常点会被平滑算法扩散到邻近区域污染更多数据。4.2 绘制 GM-ID 曲线族让曲线自己说话GM-ID 曲线族的标准画法是横轴是归一化电流密度 ID/(W/L)对数坐标纵轴是 gm/ID线性坐标每条曲线对应一个 L 值。这样画出来的曲线族从左到右依次是亚阈值区、 moderate inversion 区、强反型区。但仅仅画出 gm/ID 是不够的。实际设计中你还需要看 fT、gm/gds、Cgs 等参数。我一般会画一个 2x2 的子图矩阵左上gm/ID vs ID/(W/L)这是最核心的曲线右上fT vs ID/(W/L)决定管子的速度左下gm/gds vs ID/(W/L)决定本征增益右下Cgs/(W*L) vs ID/(W/L)决定寄生电容这样一张图基本涵盖了模拟设计需要的所有信息。画图的时候我会用不同的颜色和线型区分不同的 L并且在图例里标注 L 的值。颜色用linescolormap线型用-、--、-.、:循环保证黑白打印也能区分。figure(Position, [100 100 1200 900]); L_list [0.18 0.24 0.36 0.5 0.8 1.2 2.0 4.0]; colors lines(length(L_list)); line_styles {-, --, -., :}; for i 1:length(L_list) subplot(2,2,1); semilogx(id_norm{i}, gm_id{i}, Color, colors(i,:), ... LineStyle, line_styles{mod(i-1,4)1}, LineWidth, 1.5); hold on; subplot(2,2,2); semilogx(id_norm{i}, ft{i}, Color, colors(i,:), ... LineStyle, line_styles{mod(i-1,4)1}, LineWidth, 1.5); hold on; % ... 其他子图类似 end subplot(2,2,1); xlabel(I_D/(W/L) [A]); ylabel(g_m/I_D [1/V]); title(GM-ID 曲线族); grid on; legend(arrayfun((x) sprintf(L%.2fum, x), L_list, UniformOutput, false), ... Location, northeast);这张图跑出来之后你会发现几个有意思的现象。第一所有 L 的曲线在亚阈值区几乎重合因为亚阈值区的 gm/ID 理论上只跟工艺有关跟 L 无关。第二随着 L 增大fT 明显下降因为沟道越长渡越时间越长。第三gm/gds 随着 L 增大而增大因为输出电阻跟 L 成正比。这些现象在教科书上都有但只有自己跑出来你才会有直观的感受。4.3 从曲线到设计反查工作点的实操方法有了曲线族实际设计的时候怎么用举个例子你要设计一个单级共源放大器增益要求 40dB带宽要求 10MHz负载电容 1pF功耗预算 1mW电源电压 1.8V。第一步确定 gm/ID。增益 40dB 意味着 gm/gds 100。在 gm/gds vs ID/(W/L) 的曲线上找到 gm/gds 100 对应的 ID/(W/L) 值。假设这个值是 0.5uA那么 gm/ID 大约在 15~20 之间具体值从 gm/ID 曲线上读。第二步确定电流。功耗 1mW电源 1.8V总电流约 0.56mA。单级放大器的话这个电流全部流过输入管。第三步确定尺寸。ID 0.56mAID/(W/L) 0.5uA所以 W/L 0.56m / 0.5u 1120。如果 L 取 0.5um那么 W 560um。这个尺寸很大但为了满足增益和功耗要求是合理的。第四步验证带宽。gm (gm/ID) * ID 18 * 0.56m 10mS。带宽 gm / (2piCL) 10m / (2pi1p) 1.6GHz远大于 10MHz 的要求。说明带宽不是瓶颈可以适当降低电流来省功耗。这个反查过程如果手动在曲线上读点效率很低。我一般会在 Matlab 里写一个插值函数输入目标 gm/ID 或 gm/gds输出对应的 ID/(W/L) 和 Lfunction [id_norm, L_opt] lookup_gm_id(target_gm_id, gm_id_data, id_norm_data, L_list) % 在所有 L 的曲线中查找最接近目标 gm/ID 的点 min_error inf; id_norm NaN; L_opt NaN; for i 1:length(L_list) [~, idx] min(abs(gm_id_data{i} - target_gm_id)); error abs(gm_id_data{i}(idx) - target_gm_id); if error min_error min_error error; id_norm id_norm_data{i}(idx); L_opt L_list(i); end end end这个函数虽然简单但在实际设计中非常实用。你可以把它封装成一个 GUI滑动条调目标 gm/ID实时显示对应的 ID/(W/L) 和 L设计效率会高很多。5. 踩坑实录那些让我熬夜的报错和异常5.1 仿真不收敛从报错信息到根因定位跑 GM-ID 扫描的时候最常见的报错就是DC convergence failure。Cadence 的报错信息通常只告诉你在某个电压点不收敛但不会告诉你为什么不收敛。我的排查链路是这样的首先看报错点的电压值。如果是在 VG 接近 0 的时候不收敛那大概率是亚阈值区的数值问题。亚阈值区 ID 极小仿真器的abstol如果设得太大电流方程会变成 0 0 的退化形式导致不收敛。解决办法是把abstol从默认的 1e-12 降到 1e-15。如果是在 VG 接近 VDD 的时候不收敛那可能是管子进入了线性区gds 变得很大节点导纳矩阵的条件数变差。解决办法是加一个小的漏极电阻比如 1 欧姆或者把 VDS 设得小一点避免管子深度进入线性区。如果是在中间某个点不收敛那可能是扫描步长太大仿真器跳过了收敛域。解决办法是把扫描步长减半或者用adaptive模式。还有一种情况是仿真器报singular matrix这通常是因为某个节点没有直流通路。检查测试台确保每个节点都有到地的电阻或者电流源。提示如果反复调仿真设置都不收敛可以试试先用dc分析跑一个粗略的扫描找到不收敛的点然后在该点附近用dc的sweep模式做精细扫描。有时候不收敛只是局部现象换个扫描路径就绕过去了。5.2 数据导出为空路径、权限和格式的三重检查跑完仿真兴冲冲地导出数据结果 CSV 文件是空的或者只有表头没有数据。这种情况我遇到过好几次原因无非三个。第一路径不对。Ocean 脚本里的openResults路径是相对于当前工作目录的如果你在 ADE 里跑仿真工作目录可能是~/simulation/xxx/psf但你在终端里跑脚本工作目录是~路径就对不上。解决办法是用绝对路径或者在脚本开头加cd命令切换到正确目录。第二权限不够。Cadence 的仿真结果数据库默认是只读的如果你用ocnPrint往一个没有写权限的目录里写文件会静默失败不报错但文件是空的。解决办法是检查目标目录的写权限或者把输出目录设到/tmp下。第三格式不匹配。ocnPrint的?output参数如果指向一个已经存在的文件默认是追加模式不是覆盖模式。如果你之前跑过一次文件里已经有旧数据新数据会追加在后面Matlab 读进来会混在一起。解决办法是在脚本开头用delete命令删掉旧文件或者用?mode overwrite参数强制覆盖。5.3 曲线异常从物理直觉反推仿真设置有时候数据导出来了画出来的曲线却不对劲。比如 gm/ID 曲线在亚阈值区不是平的而是往上翘或者 fT 曲线在某个 L 下突然掉下去。这些异常往往不是数据处理的锅而是仿真设置有问题。gm/ID 在亚阈值区往上翘通常是因为 VDS 太小管子没有完全进入饱和区gds 的影响混进了 gm 里。解决办法是把 VDS 调大或者检查测试台里漏极电压源的设置。fT 曲线突然掉下去可能是某个 L 下的仿真没有收敛数据点全是 NaNMatlab 插值之后画出来就是一条直线或者跳变。解决办法是回到 Cadence 里检查那个 L 的仿真日志看有没有收敛警告。还有一种情况是曲线在 moderate inversion 区出现台阶这通常是扫描点不够密导致的。解决办法是增加扫描点数或者用log扫描代替线性扫描。我一般会在 Matlab 里加一个自动检查函数对每条曲线做单调性检查和连续性检查发现异常就打印警告提示我回去检查仿真设置。这个函数帮我省了很多来回折腾的时间。6. 效率提升脚本化、模板化和自动化6.1 把重复劳动交给脚本GM-ID 流程里最耗时的不是仿真本身而是重复的设置和导出操作。如果你每次都要手动在 ADE 里设参数、跑仿真、导出数据、写 Matlab 脚本一天下来也跑不了几个工艺角。我的做法是把整个流程脚本化。Cadence 端用 Ocean 脚本把测试台搭建、仿真设置、数据导出全部自动化。Matlab 端用一个主脚本自动读取指定目录下的所有 CSV 文件做数据清洗、绘图、保存图片。整个流程只需要改几个配置参数比如工艺库路径、L 的列表、输出目录然后一键运行。Ocean 脚本的自动化程度可以做到什么地步我给你看一个我常用的模板; 配置区 lib_name my_tech_lib cell_name nmos_gm_id L_list list(0.18 0.24 0.36 0.5 0.8 1.2 2.0 4.0) VDD 1.8 W 10.0 ; 打开设计 dbOpenCellViewByType(lib_name cell_name schematic r) ; 设置仿真环境 design(my_tech_lib nmos_gm_id) simulator(spectre) analysis(dc ?saveOppoint t ?sweepVar VG ?start 0 ?stop VDD ?step 0.01) ; 参数扫描 foreach(L L_list desVar(L L) desVar(W W) run() ; 导出数据 selectResult(dc) ocnPrint(?output sprintf(nil gm_id_L_%g.csv L) ?numberNotation scientific vg id gm gds cgs cgd) )这个脚本跑一次就能把所有 L 的数据全部导出。你只需要在 Matlab 端写一个循环把 CSV 文件读进来就能出图。6.2 模板化让每次新工艺都能快速上手换一个工艺GM-ID 流程要重新跑一遍。如果每次都从头搭测试台、写脚本效率太低。我的做法是做一个模板工程包含测试台原理图、Ocean 脚本、Matlab 脚本三部分。换工艺的时候只需要改几个地方工艺库的名字、管子的模型名、电源电压、L 的列表。其他部分原封不动。模板工程的关键是参数化。测试台里的管子模型名不要写死用desVar定义成变量Ocean 脚本里的路径和文件名用变量拼接Matlab 脚本里的工艺参数放在一个单独的配置文件里。这样换工艺的时候只需要改配置文件不用动核心代码。我还会在模板里预置几个常用的工艺角比如 TT、SS、FF、SF、FS。跑 GM-ID 的时候顺便把几个工艺角都跑一遍这样后续设计的时候可以直接看工艺角之间的差异不用等到最后才做 corner 仿真。6.3 自动化检查让脚本帮你发现异常数据导出之后不要急着画图。先让脚本做一轮自动检查看看数据有没有问题。我一般会检查这几项数据点数量是否足够每个 L 至少 100 个点ID 是否单调递增随 VG 增大gm/ID 是否在合理范围内亚阈值区 20~30强反型区 5~15fT 是否为正数且单调在强反型区是否有 NaN 或 Inf如果任何一项检查不通过脚本会打印警告并跳过对应的 L避免污染后续的绘图和分析。这个自动检查帮我发现了好几次仿真设置的问题比如某个 L 下 VDS 设错了导致管子没进饱和区。7. 从 GM-ID 曲线到实际设计决策7.1 工作点选择的权衡增益、速度和功耗GM-ID 曲线最大的价值是让你在设计初期就能看清增益、速度、功耗三者之间的权衡关系。在 gm/ID vs ID/(W/L) 的曲线上从左到右gm/ID 从 30 降到 5。gm/ID 越高意味着同样的电流能获得更高的跨导也就是更高的增益和更好的噪声性能。但高 gm/ID 对应的是低电流密度管子工作在亚阈值区或 moderate inversion 区速度慢匹配差。反过来gm/ID 越低管子工作在强反型区速度快匹配好但增益低噪声大。所以工作点的选择本质上是在模拟性能和速度/匹配之间找平衡。我的经验是对于低功耗、高增益的电路比如生物医疗信号链的前置放大器gm/ID 取 20~25让管子工作在 moderate inversion 区对于高速电路比如射频前端gm/ID 取 8~12让管子工作在强反型区对于通用电路gm/ID 取 15 左右折中。7.2 用 gm/gds 曲线确定本征增益上限gm/gds 曲线告诉你在给定的 L 和 ID/(W/L) 下管子的本征增益最大能到多少。这个值决定了单级放大器的增益上限。如果你需要 60dB 的增益单级放大器肯定做不到本征增益通常只有 40~50dB必须用两级或者 cascode 结构。在 gm/gds vs ID/(W/L) 的曲线上你会发现一个有趣的现象gm/gds 随着 ID/(W/L) 减小而增大在亚阈值区达到最大值。但这个最大值受限于 LL 越大gm/gds 越大。所以如果你需要高增益除了降低电流密度还可以增大 L。但增大 L 会降低 fT影响速度。所以高增益和高速是一对矛盾。GM-ID 曲线让你能定量地看到这个矛盾L 从 0.18um 增大到 4umgm/gds 可能从 20 增大到 100但 fT 可能从 50GHz 降到 2GHz。你需要根据具体指标来选 L。7.3 从曲线到网表尺寸计算的完整示例假设你要设计一个两级运放第一级是差分输入级第二级是共源放大级。指标要求开环增益 80dB单位增益带宽 50MHz相位裕度 60 度负载电容 2pF功耗 2mW电源 1.8V。第一步分配增益。两级运放的总增益是两级增益之积。假设第一级增益 40dB第二级增益 40dB。第一级用 cascode 结构本征增益需要 60dB留 20dB 余量给反馈第二级用共源结构本征增益需要 50dB。第二步确定第二级的 gm/ID。第二级需要驱动 2pF 负载单位增益带宽 50MHz所以第二级的 gm 至少是 2pi50M*2p 0.63mS。第二级电流假设 0.5mA那么 gm/ID 0.63m/0.5m 1.26这个值太低了说明第二级电流不够。增大电流到 1mAgm/ID 0.63还是太低。实际上gm/ID 最低也就 5 左右所以第二级电流至少是 0.63m/5 0.126mA。取 0.2mAgm/ID 3.15还是偏低。取 0.5mAgm/ID 1.26不对我算错了。gm 0.63mSID 0.5mAgm/ID 0.63/0.5 1.26 V^-1这个值确实太低了。正常 gm/ID 在 5~25 之间所以 ID 应该是 0.63m/5 0.126mA 到 0.63m/25 0.025mA。取 ID 0.1mAgm/ID 6.3合理。第三步确定第二级的 L。需要本征增益 50dB也就是 gm/gds 316。在 gm/gds 曲线上找到 gm/gds 316 对应的 L 和 ID/(W/L)。假设 L 1um 时gm/gds 最大能到 200不够L 2um 时gm/gds 最大能到 400够了。所以 L 取 2um。ID/(W/L) 从曲线上读假设是 0.1uA那么 W/L 0.1m/0.1u 1000W 2000um。这个尺寸很大但为了高增益没办法。第四步验证。gm 6.3 * 0.1m 0.63mS带宽 0.63m/(2pi2p) 50MHz刚好满足。增益 20*log10(316) 50dB满足。这个计算过程如果手动在曲线上读点很繁琐。但如果你把曲线数据存在 Matlab 里写一个查找函数几秒钟就能算出来。这就是 GM-ID 方法配合 Matlab 可视化的效率优势。8. 一些个人体会和后续扩展方向这套 GM-ID 流程我用了好几年最大的感受是它把模拟设计从试参数变成了查曲线。以前调一个运放可能要跑几十次 DC 仿真每次改一个 W 或者 L看增益和带宽的变化。现在只需要跑一次 GM-ID 扫描后面所有设计都在曲线上做效率提升非常明显。有几个小技巧是我踩过坑之后总结的。第一跑 GM-ID 的时候顺便把 Cgs、Cgd、Cdb 这些寄生电容也导出来后续算带宽和零极点的时候直接用不用再跑 AC 仿真。第二L 的列表不要取太多5~8 个就够了太多的话曲线太密看不清。第三Matlab 绘图的时候把数据点和拟合曲线分开画数据点用散点拟合曲线用实线这样能看出插值的质量。后续如果想进一步扩展可以往几个方向走。一是把 GM-ID 流程和gm/ID设计法结合做一个自动化的尺寸计算工具输入指标输出网表参数。二是把工艺角数据也纳入进来画一个工艺角包络看设计在 worst case 下的裕度。三是把数据导出成 JSON 或者 HDF5 格式方便和其他工具链集成。这套流程不复杂但细节很多。希望这篇总结能帮你少走一些弯路。如果你在跑 GM-ID 的时候遇到什么奇怪的问题欢迎一起交流。