ARTICLE DETAIL

资讯详情

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

基于Cadence SKILL的参数化螺旋电感PCell实现指南

基于Cadence SKILL的参数化螺旋电感PCell实现指南 做模拟版图或者射频layout的兄弟肯定经历过这种痛苦电感要一圈一圈画每圈线宽、间距、转角都得盯仔细画完发现圈数不对或者尺寸要改又得全部删掉重来。尤其到了后仿阶段电感感值差个几百pHlayout就得推倒重画。这种活干多了人很容易麻。后来我实在受不了花了一个周末用Cadence SKILL写了个参数化电感PCell把“手动画电感”这件事彻底干掉。输入线宽、间距、圈数、起始边长它自己把螺旋生成好想改参数直接重跑函数几秒钟出新版图。这篇就把我的实现思路、完整可跑的SKILL代码、以及封装成参数化PCell的方法全部放出来顺便聊聊我踩过的坑。适合正在用Cadence Virtuoso画RF电感、滤波器电感、以及想入坑SKILL脚本的版图工程师。代码直接能在CIW里load不需要额外装东西。1. 为什么我决定自己写电感PCell1.1 手动画电感的“反人类”细节我先说说手动场景有多烦。以一个四圈方形螺旋电感为例你至少要画十六条线段每条线段的长度都不一样而且每往里一圈长度都要减去线宽间距的两倍。哪怕你只是改一个间距值内圈所有线段长度全部连锁变化光算长度就要算半天。更恶心的还在后面。画完之后如果要仿真通常要建一个参数化cell方便在ADE里扫圈数、扫线宽。手画的话每次仿真参数一变layout就得手动跟着改改完还要重新跑DRC/LVS。项目周期紧的时候这种重复劳动非常消磨耐心。所以我当时给自己的要求是写一个函数把电感的所有几何参数全部变成输入变量跑一次就能得到完整螺旋参数变化后重跑一次不用手动去调任何一条边。1.2 PCell到底能帮你省多少事PCellParameterized Cell在模拟版图里其实很常见比如PDK里的电阻、电容、MOS管大多都是PCell。它的好处很直接版图由一个参数集驱动改参数等于重生成图形而且可以挂到CDF上在原理图或者版图里直接填参数就能生成对应器件。电感这种结构复杂、参数敏感、形状重复的器件天生适合做成PCell。有了PCell之后你可以在同一个cell里快速尝试不同圈数、不同线宽、不同间距甚至批量生成一组候选尺寸喂给电磁仿真做扫描。这一步在RF匹配、LC-VCO、滤波器设计中能省下大量时间。1.3 为什么用SKILL而不是pcell菜单里自带工具Virtuoso里确实有一些画螺旋的菜单和skill函数比如rodCreateSpiral但实际经验是工艺一来layer名不一样slot/切角规则不一样默认函数生成的东西经常不满足DRC或者画出来的形状不是你要的方形螺旋。自己用SKILL写的好处是每一段线、每一个转角都完全可控后期加切角、加底层金属引出、加通孔阵列都方便改。2. 设计前必须想清楚的几个关键点2.1 螺旋形状选方形还是八边形这个选择会影响代码复杂度和仿真性能。方形螺旋代码最简单顶点数量少生成速度快在初版设计阶段完全够用。八边形和圆形在高频下电流分布更均匀寄生电容更小但写成PCell的复杂度要高不少因为每个顶点的坐标都要做三角函数计算。我的建议是新手先实现方形螺旋把参数化流程跑通再改造成八边形。毕竟PCell的核心价值在“参数化流程”而不是某一圈画得多漂亮。后文代码就是方形螺旋。2.2 金属层选择不是随便填的CMOS工艺里做电感一般用顶层厚金属因为它距离衬底远衬底损耗小而且厚金属的方块电阻低Q值更容易做高。实际填layerName的时候不要想当然写“metal1”要去tf里确认当前工艺对应名字比如Metal6、AP、TM2这类。代码里如果layer名写错rodGetLayer会返回nil运行直接报错。我见过不少同学第一次跑电感脚本卡在layer这一关其实就是工艺层名没对上。所以我在代码里专门加了层名检查找不到层直接报错避免生成一个空荡荡的shape。2.3 感值初估公式到底该画多大尺寸写代码之前最好先大体估算一下目标感值对应什么尺寸否则会画出一个面积巨大但是感值完全不对的电感。平面方形螺旋电感有个经典近似公式[ L \approx \frac{\mu_0 n^2 d_{avg}}{2} \left( \ln \frac{2.46}{\rho} 0.2\rho^2 \right) ]n圈数d_out外圈边长d_in内圈边长d_avg (d_out d_in) / 2ρ (d_out - d_in) / (d_out d_in)举个实际例子。假设我想做1nH左右的电感取外圈边长300μm内圈边长100μm那么d_avg 200μm 2e-4米ρ (300-100)/(300100) 0.5代入公式每圈不同圈数的估算结果大概是n1时L约0.21nHn2时L约0.83nHn3时L约1.86nH所以目标1nH用两圈半到三圈比较靠谱。注意这只是空气芯的粗略估算实际硅工艺要考虑衬底涡流、寄生电容、趋肤效应最终肯定还要用HFSS或者ADS的EM仿真修正但初估能帮你把几何参数定在一个合理范围内避免无头苍蝇乱试。3. 完整SKILL代码从设计思路到可直接运行3.1 算法核心思想长度递减法方形螺旋看起来复杂其实有个特别朴素的生成规则从外圈开始先向右画一段再向上画一段再向左、向下这样每转一圈下一次的长度都减少线宽间距。因为每一圈线宽占了一部分间距占了一部分所以往内收的时候螺旋的边长自然比上一圈短。这个算法用代码实现只有一个变量在变当前线段长度。方向按“右→上→左→下”循环长度每走一段就减掉(ws)。四圈就是16段八圈就是32段逻辑完全一致。3.2 可运行的完整代码下面是完整版代码我已经注释得比较详细。用到的核心函数是rodCreatePath这个函数可以直接按一串顶点生成带线宽的path比手动用rodCreateRect拼矩形简单太多。/* generateRectSpiral.il * 自动生成方形螺旋电感 * 适用于 Cadence Virtuoso Layout Editor */ procedure( createRectSpiralInductor( optional (layerName metal1) ; 金属层名需要和当前工艺的layer名一致 (startX 0.0) ; 起点X坐标版图单位通常是微米 (startY 0.0) ; 起点Y坐标 (initLength 200.0) ; 第一段边长 (lineWidth 5.0) ; 线宽 (spacing 5.0) ; 线与线间距 (turns 3) ; 圈数 (minSeg 10.0) ; 最内段最小保护长度 ) let( (layerId pts curX curY curLen dx dy i) /* 检查initLength是否足够大 */ when( initLength (4.0 * turns - 1.0) * (lineWidth spacing) minSeg error(initLength too small! Need at least %f um\n (4.0 * turns - 1.0) * (lineWidth spacing) minSeg) ) /* 检查金属层名是否存在 */ layerId rodGetLayer(layerName) when( !layerId error(Layer %s not found in current techfile.\n layerName) ) /* 初始化顶点列表和当前坐标 */ pts list( list(startX startY) ) curX startX curY startY curLen initLength /* 循环生成顶点 */ for( i 1 4 * turns case( mod(i 4) (1 dx 1.0 dy 0.0 ) (2 dx 0.0 dy 1.0 ) (3 dx -1.0 dy 0.0 ) (0 dx 0.0 dy -1.0 ) ) curX curX dx * curLen curY curY dy * curLen pts append1( pts list(curX curY) ) /* 每段长度递减线宽间距 */ curLen curLen - (lineWidth spacing) ) /* 按顶点列表生成path */ rodCreatePath( ?name ind_spiral ?layer layerId ?width lineWidth ?pts pts ?fill t ) printf(Created rectangular spiral: turns %d, first seg %f um\n turns initLength) ) )3.3 参数含义和推荐取值范围代码里的参数定义如下参数名含义取值范围建议layerName金属层名按工艺tf填写如M6/AP/TM2startX / startY螺旋起点坐标通常填(0,0)看布局需要initLength第一段边长建议 2turns(ws) 以上lineWidth线宽满足最小线宽常用3~10μmspacing线间距满足最小间距常用2~6μmturns圈数1~8圈圈数太多面积和损耗都上去minSeg最内段最小长度建议不低于最小制造尺寸特别注意initLength这个参数。因为每段长度都在减少如果初值设太小画到最后几段长度变成负的螺旋会乱掉。所以代码里加了保护判断initLength必须大于(4turns-1)(ws)minSeg否则直接报错。3.4 在Virtuoso里运行它运行方式很简单。打开CIW窗口执行load generateRectSpiral.il然后调用函数createRectSpiralInductor(metal1 0.0 0.0 200.0 5.0 5.0 3)执行之后当前打开的版图cellview里就会自动出现一个三圈方形螺旋电感第一段边长200μm线宽5μm间距5μm。如果你IDE里不想手敲也可以在CIW里用load加载后直接调用函数并观察结果。注意执行前当前窗口必须处于编辑状态否则rodCreatePath找不到目标cellview。3.5 坐标位置不对怎么办默认起点是由startX/startY决定的。上面例子用(0,0)螺旋整体向右上方展开位于第一象限。如果你想把螺旋放到某个固定位置直接改startX和startY就行。我在实际使用中喜欢把起点放在某个PAD或者外部连线的接入点这样整个电感的端口正好落在我要的位置后面接传输线或者焊盘都方便。这个习惯强烈建议保持比生成完再整体移动省事。4. 从“一次性脚本”升级成真正的参数化PCell4.1 用CDF callback实现参数驱动上面的函数每次都要手动load、手动调用严格来说还不算PCell。但是它已经是参数化的了所以我们只需要再加一层“外壳”把参数挂到CDF上并写一个callback参数一变就调用函数重新生成版图。简单说CDF是Cadence里描述器件属性的机制每个参数都可以绑定一段SKILL callback。当你在版图或原理图里修改参数值时callback会自动执行。下面给一个callback示例它的作用是先删掉cellview里所有旧shape再调用上面的螺旋生成函数重新画。procedure( indSpiralCdfCallback( layerName initLength lineWidth spacing turns ) let( (cvId) cvId geGetEditCellView() when( cvId /* 清除当前cellview里的旧shape */ mapcar( lambda( (shape) dbDeleteObject(shape) ) cvId~shapes ) /* 重新生成螺旋 */ createRectSpiralInductor( layerName 0.0 0.0 initLength lineWidth spacing turns 10.0 ) ) ) )把这个callback绑到CDF参数上之后你在property form里改一下turns从3改成5版图里的螺旋会自动删除旧的、生成新的。每次修改参数都能立刻看到更新结果。4.2 真正PCell和Callback的差别上面方案对于个人使用完全够用。但如果是做PDK发布给团队用建议做成真正的Skill PCell让它在dbOpenCellViewByType时被动态evaluate而不是依赖callback手动触发。真正PCell的核心要求是所有图形必须基于参数动态计算参数没变时用cache参数变了自动重算。用Skill实现时一般是在PCell evaluator里调用rodCreateRect、rodCreatePath这些函数并把用到参数的关键信息比如坐标点通过rodMapToMem注册到PCell对象里。这一块坑比较多我当时的经验是先别急着研究pcell evaluator的完整底层机制先用CDF callback把流程跑通。等确认你的几何生成算法没问题了再花时间封装成标准PCell研究pcellEval、rodMapToMem这些API。4.3 加切角、加底层引出PCell的进阶方向方形螺旋最内圈的直角拐角在实际流片中会有比较明显的电流拥挤高频下影响Q值。进阶PCell可以在每个转角处做45度切角这在Skill里可以用dbCreateChamfer倒角实现或者在生成顶点时直接构造八边形拐点坐标。另外还有一个高频痛点电感内部终点如何引出来。同一层金属从中心往外走一定会和螺旋线交叉短路所以通常的做法是螺旋的主体用顶层厚金属画内部终点用下一层金属引到外部中间叠加一排通孔。这部分逻辑强烈建议也做成可选参数比如?crossUnder t在PCell里自动生成底层金属路径和via数组可以极大减少手动拼接的时间。5. 实际项目中经常踩的坑和排查方法5.1 rodGetLayer返回nil最常见的问题没有之一。报错信息是Layer xxx not found。原因基本都是layerName填错。处理方式去CIW里执行techGetLayerFile或者直接看tf文件确认当前工艺里顶层厚金属叫什么。有些工艺的顶层厚金属叫M6有些叫AP还有些叫TM2不要凭经验猜。也有一种情况是当前cellview没绑定techfile这时候任何layer都查不到先检查cellview是否属于某个library。5.2 螺旋生成后线条之间短路如果spacing设置太小或者rodCreatePath在拐角处做了重叠填充可能出现相邻圈之间贴在一起的情况。Skill的?fill t默认会填充所有转角线宽较宽时转角内侧会被多余填充覆盖等效间距变小。解决办法检查实际生成后相邻圈之间的最小间距是否满足DRC尝试把?fill nil看拐角是否恢复正常在参数表里把spacing留出20%~30%的余量不要正好压在最小间距上。5.3 最内圈段长太小制造时直接违反density规则前面代码里我加了minSeg保护但即使不报错最内圈的边长如果只有几个微米金属密度可能不达标也会在金属density检查时被卡。建议把minSeg默认值设成工艺最小金属宽度的3倍以上。另外可以在生成后用dbGetCard确认一下每圈的实际尺寸提前发现这个问题。5.4 改参数后旧图形没删干净如果把callback挂在CDF上最容易忽略的一点每次callback执行时要先删除旧图形。如果只生成不删除改几次参数后cellview里会叠一堆螺旋看起来像鬼影。我写的callback里用的是mapcar( lambda( (shape) dbDeleteObject(shape) ) cvId~shapes )这会把cellview里的所有shape全部删掉。如果你的cell里还有其他图形建议改成按shape~name筛选只删ind_spiral开头的shape。5.5 单位制搞混画出来的尺寸放大1000倍Cadence的坐标单位默认是user unit常见是micron但某些工艺库或者选项可能设置为nm。如果你的initLength填的是200在nm单位下就是200nm小得离谱。排查方式画完之后量一下第一段边长看是否等于你输入的initLength。如果差1000倍就是user unit设置问题。统一在CF里设置setUserUnit(micron)或者用带单位的写法比如200u避免手滑。5.6 Q值和感值都对不上仿真结果SKILL画的几何形状没问题但EM仿真结果和初估差很多这种情况非常常见。原因不在PCell而在建模没有加ground shield衬底涡流把Q值拉低没有考虑馈线长度带来的附加电感底层金属引出线的电阻和电感没有并进模型。经验是PCell只负责把几何画对电感的电磁性能必须靠EM仿真迭代。这个步骤省不掉。写在最后画完这个PCell之后我最大的感受是真正花时间的不是写SKILL那几行代码而是第一次想清楚“螺旋每一圈到底是怎么递推出来的”。想明白了这一点后面无论是改八边形、加切角、接地shield都是顺水推舟的事。如果你现在也经常被重复性的版图结构折磨真心建议抽一晚上把手上最常画的结构写成一个参数化函数哪怕不封装成PCell只是留一个skill脚本在CIW里调用也能省下后面几个星期的手工劳动。参数化思维这套东西值得每个做版图的人拥有。
返回列表