用Cadence Virtuoso搭建晶体管级CMOS全加器:从反相器到仿真验证
很多初学集成电路的同学在数电课上把真值表和卡诺图背得滚瓜烂熟结果第一次打开Cadence Virtuoso面对空白原理图反而不知道从哪里下手。这篇内容就来解决这件事用一套教学用的180nm工艺库从晶体管级的CMOS反相器开始逐个搭建与非门、或非门、异或门最后组合出一个1-bit全加器并在Virtuoso的ADE L环境里跑Spectre瞬态仿真把加法器输入输出波形和真值表一条条对上。整个过程覆盖了Virtuoso原理图编辑、工艺库挂载、符号创建、测试bench搭建、激励设置和波形分析适合集成电路方向的高年级本科生、刚进实验室的硕士生也适合想补一补定制数字电路flow的工程师。如果你只想用Verilog快速验证逻辑用VCS或Questa会更轻松但如果你想真正理解加法器在晶体管层面怎么工作、延迟从哪里来、管子宽长比到底有什么影响这条从晶体管到加法器的路线很适合你。1. 环境准备让Virtuoso和180nm工艺库先跑起来1.1 一套能用的环境是第一步开始之前先确认你有一台能跑Cadence的Linux工作站或者远程服务器。Virtuoso本身的安装和维护一般由实验室或公司的EDA管理员处理不需要我们自己装但我们要确认几个前提条件Cadence IC6系列版本能用比如IC617或者IC618仿真器是Spectre或包含Spectre的MMSIM套件同时有对应的工艺PDK标题里说的180nm工艺库教学场景里最常见的是Cadence自带的gpdk180这类通用PDK也可能是学校流片合作方给的0.18um工艺PDK。不管具体是哪一套操作流程几乎一样。硬件方面不需要太夸张内存16GB以上会比较舒服因为Virtuoso界面本身吃资源Spectre仿真虽然不占太多内存但大一点的testbench加上波形显示内存太小会让整个工具卡成PPT。另外特别提醒一句工作目录路径不要带中文和空格Linux下很多EDA工具对这些字符处理得很不友好。如果你用的是服务器注意确认自己有写权限的目录不要在只读目录下面建库。1.2 工艺库挂载与启动配置拿到PDK之后第一件事不是打开Virtuoso而是确认启动配置是否正确。Cadence Virtuoso在启动时会读取当前工作目录下的几个关键文件.cdsinit、.cds.lib、display.drf。很多PDK会自带一套初始化脚本在启动前source一下就行比如部分工艺库要求执行virtuoso前先跑一下csh setup.csh或者把PDK的skill脚本写进.cdsinit。如果你是自己手动挂载.cds.lib文件里通常至少要有两行INCLUDE $PDK_HOME/cadence/cds.lib DEFINE lab ./lab第一行把PDK自带的库引用进来第二行定义一个自己的工作库用来放后面所有自建cell。这里的$PDK_HOME要根据实际安装路径写死不同PDK的目录结构差异很大最稳的办法是参考PDK自带的README或者安装文档。还有一点容易被忽略.cdsinit里会自动加载一些常用快捷键和显示资源文件。如果启动后发现原理图窗口颜色怪怪的或者元件symbol样式不对多半是display.drf没有正确加载。这类问题不会影响仿真结果但会影响画图和查看波形的心情所以配置环境时花几分钟确认一遍是值得的。注意不同PDK的库名和模型名差异很大我这里写的是通用思路。如果你用的库在Library Manager里看不到对应的技术库techLib检查一下.cds.lib的INCLUDE路径是不是写对了。1.3 新建工作库并关联工艺库启动Virtuoso后打开Library Manager。在Library Manager里选择File - New - Library输入库名比如lab。创建时有一个关键选项选“Attach to an existing technology library”然后在列表里选择你的PDK技术库。这样做的意义在于新库会自动继承PDK的设计规则、显示资源和仿真模型映射关系后面画原理图时调用的晶体管CDF参数才是完整的。如果没有attach虽然也能放置nmos和pmos但仿真时很容易出现模型找不到的错误。之后在这个lab库里新建cell用Cellview类型schematic。建议给每个模块单独建一个cell比如inv、nand2、nor2、xor、adder1bit和tb_adder层次化设计在后面推进时会非常方便。创建cell时记得选好library和view别把逻辑反了。2. 原理先行全加器的逻辑到底怎么落到CMOS管子上2.1 先列真值表和布尔式1-bit全加器做的是带进位的二进制加法。输入是A、B、Cin来自低位的进位输出是S和和Cout向高位的进位。真值表如下ABCinSCout0000000110010100110110010101011100111111把真值表化简成布尔表达式最常用的写法是S A XOR B XOR Cin Cout (A AND B) OR (Cin AND (A XOR B))也可以把Cout写成三输入多数投票逻辑Cout majority(A,B,Cin) AB ACin BCin。这两种表达式等价但在门级实现里用A XOR B这个中间信号来搭Cout会更省门因为S本身就需要两个异或门A XOR B的结果是现成的。2.2 为什么用CMOS反相器、与非门、或非门做积木从晶体管开始搭加法器肯定要先把基本门电路设计出来。静态CMOS逻辑的核心思想是用互补的PMOS上拉网络和NMOS下拉网络实现逻辑函数PMOS网络连接VDD负责给输出充到高电平NMOS网络连接GND负责把输出拉到低电平。两种管子不会同时导通所以静态功耗的理论值几乎为零这也是CMOS成为数字电路主流工艺的根本原因。反相器最简单一个PMOS的栅极接输入源接VDD漏接输出一个NMOS的栅极也接输入源接GND漏接输出。输入为0时PMOS导通、NMOS关断输出被拉到1反之亦然。二输入与非门NAND2的结构是两只PMOS并联、两只NMOS串联。并联意味着只要A或B任一为0上拉网络就导通输出为1只有当A和B都为1时两个串联NMOS都导通输出才为0。二输入或非门NOR2正好相反两只PMOS串联、两只NMOS并联。这两种门加上反相器理论上可以组合出任何组合逻辑函数用它们做全加器的积木是完全够用的。2.3 管子尺寸为什么P管明显比N管宽在180nm工艺、1.8V电源电压下器件的阈值电压大概在0.4V到0.5V左右。设计CMOS门电路时管子宽长比W/L是很关键的参数。表面上看数字逻辑只要保证管子能开能关就行但实际上上升时间和下降时间是否对称直接影响门延迟和功耗。硅材料里空穴的迁移率只有电子的三分之一左右所以PMOS的导通能力天然比NMOS弱。为了让PMOS上拉的速度和NMOS下拉的速度尽量匹配我们通常把PMOS的沟道宽度做到NMOS的2到3倍。比如我这里N管取W 1.2uP管取W 3.6u或者2.4uL统一取工艺允许的最小值180n。这样串接门电路时上升沿和下降沿的斜率会比较接近。注意如果你做的是纯数字标准单元库设计P管和N管的具体比例会有更精细的优化但教学和验证用2到3倍已经完全够用。2.4 从门到加法器的组装路线确定了基本门之后用它们搭全加器我这里采用一条层次非常清晰的路线底层用晶体管画反相器、与非门、或非门中间层用四个与非门搭一个异或门顶层把两个异或门和若干个基础门组合成全加器。虽然这不是晶体管数量最少的方案真实标准单元库里会用复合门或者传输门优化到十几或二十几个管子但教学场景里门级拼装的可读性和可调试性是最好的每一步连线都能对着表达式检查。用与非门搭异或门的经典公式XOR(A,B) NAND( NAND(A, NAND(A,B)), NAND(B, NAND(A,B)) )也就是四个二输入与非门第一个NAND先算NAND(A,B)第二和第三个NAND分别把A、B和中间信号做与非最后一个NAND把两个结果合并成最终输出。搭Cout时先算AB再算Cin AND (A XOR B)最后用或门合并。这样整个全加器使用的门数量和连线关系完全确定。3. 手把手搭建原理图从管级反相器到全加器符号3.1 新建库和第一个晶体管级电路反相器在Library Manager里先在lab库下新建一个inv的schematic cell。打开原理图编辑器后用菜单Place - Instance或者直接按快捷键i在Library Browser里找到PDK技术库下的模拟器件放一个pmos4和一个nmos4。这里要特别说明不同PDK里晶体管的cell名字可能不一样有的是pmos4/nmos4有的是pmos/nmos还有的可能叫pch/nch以你自己PDK里的名字为准。放置管子后要在属性窗口里设置CDF参数。最重要的几个Model name、Width、Length和Finger。Model name一般会自动带入不用改Width按前面说的设成P管2.4u、N管1.2uLength都设成180nFinger等于1。如果PDK支持Multiplier也保持1就行。接下来连线。输入pin叫A输出pin叫Y两个管子的栅极连在一起接A两个管子的漏极连在一起接Y。PMOS的源极接VDDNMOS的源极接GND。衬底连接千万别漏PMOS的bulk接VDDNMOS的bulk接GND。在Virtuoso原理图里VDD和GND可以直接用VDD/GND的pin符号也可以创建一个叫vdd!和gnd!的pin靠同名pin自动连接。我习惯用IPIN库里的vdd和gnd符号这样仿真时电源电压可以由testbench里的独立电压源提供灵活性更高。画完检查一遍菜单Check and Save确认没有悬空端和错误连线。然后把这个反相器生成symbolCreate - Cellview - From Cellview选择输入在左、输出在右的引脚摆位symbol会自动生成。这样后面就能像搭积木一样复用inv这个模块。3.2 搭nand2和nor2一个串联一个并联接着新建nand2。仿真里可以先把足够大的PMOS/NMOS比例关系放到参数窗口例如N管W1.2uP管W2.4u。NMOS两个管子串联在下拉网络第一个NMOS源极接GND漏极接第二个NMOS的源极第二个NMOS的漏极接输出Y两个NMOS的栅极分别接A和B。PMOS两个管子并联在上拉网络两个PMOS的源极都接VDD漏极都接输出Y栅极分别接A和B。这样结构就对了A和B同时为1时下拉网络导通输出为0其他任意情况下至少有一个PMOS导通输出为1。nor2结构是镜像的NMOS两个管子并联源极都接地漏极都接输出PMOS两个管子串联第一个PMOS源极接VDD漏极接第二个PMOS的源极第二个PMOS的漏极接输出。A和B都为0时两个串联PMOS导通输出被上拉到1只要A和B有一个为1对应NMOS导通输出被拉低。在这里有一个非常容易犯的错串联分支里管子源漏接反。MOS管的源漏在原理图中其实有定义尤其在串联结构里如果接反可能导致某些偏置条件下管子行为异常。Virtuoso的原理图仿真对源漏极性有一定容忍度但最好还是按照标准画法接别给后续版图设计埋坑。两个门都需要生成symbol。生成symbol时检查引脚方向A、B是inputY是output其余不用。3.3 用与非门搭异或门新建xorcell取消勾选创建新view时的schematic只放我们已经做好的nand2symbol。在原理图里放四个nand2用导线按照前面的公式连接。连线顺序这样理第一个nand2的输入接A和B输出为中间信号nAB也就是A NAND B第二个nand2的输入接A和nAB输出为中间信号q1第三个nand2的输入接B和nAB输出为中间信号q2第四个nand2的输入接q1和q2输出就是XOR的结果。这样电路接起来逻辑非常清楚检查也方便。在这个模块上除了基础的门级连线不需要额外调整尺寸因为nand2内部已经设置好了。生成symbol命名为xor输入A、B输出Y。你可以先不做仿真等到顶层一起验证如果你担心中间层出错也可以先建一个临时testbench单独测不过对新手来说多一次仿真多一点经验建议后面统一验证。3.4 顶层全加器层次化组装新建adder1bitschematic开始放模块。先放两个xorsymbol第一个xor的输入接A和B输出为p表示A XOR B第二个xor的输入接p和Cin输出为S。这样S A XOR B XOR Cin解决了求和。Cout电路按下面的方法搭用一个nand2和一个inv组成AND门计算A AND B输出为ab_out。再用一个nand2和一个inv组成AND门计算Cin AND p输出为cp_out。最后用一个nor2加一个inv组成OR门输入接ab_out和cp_out输出接Cout。到这里全加器的晶体管级顶层原理图实际上是层次化的顶层只看到门级symbol真正的管子都在底层。如果你对“从晶体管到加法器”这个过程有执念可以把顶层往下打散但层次化设计是行业内的标准做法也是EDA工具处理大规模电路的基本方式。尤其是后续学习版图设计时层次化结构能让LVS和寄生提取轻松很多。放置完所有模块后添加顶层pinA、B、Cin是inputS、Cout是output。然后把所有用到的信号连好Check and Save确认无误。3.5 搭测试环境testbench仿真不是直接把顶层symbol丢给Spectre就能跑的需要一个测试平台也就是testbench。新建tb_addercellschematic类型。在tb_adder里放一个adder1bitsymbol然后添加激励源。电源用一个idealdc源接1.8V给VDD地端接到GND。三个输入A、B、Cin分别用vpulse或者vsource产生方波。我这里建议在输出端加一个20fF左右的理想电容到GND用cap库里的mimcap或者理想的capsymbol都可以。为什么要加这个负载电容因为实际电路后面会连到下一级门任何门都有输入电容加上负载电容后测量的传输延迟更有参考意义。如果不加负载电容输出波形会更陡但你在仿真里其实看不出门延迟随负载的变化也就少了一个理解延迟机制的机会。pin可以不加直接用网络名就行比如VDD、GND、A、B、Cin、S、Cout后续仿真选net时更方便。4. 仿真验证配置ADE L跑transient4.1 在ADE L中选模型和仿真器打开全加器testbench的schematic窗口然后Launch - ADE L进入模拟设计环境。首先在Setup - Simulator/Directory/Host里确认Simulator是spectre。接着Setup - Model Libraries点击Browse找到PDK对应的模型文件常见的是.scs后缀或者.lib后缀比如gpdk180.scs。选中后在Section栏填tt典型工艺角表示采用typical-typical工艺参数。如果你的PDK分ss/ff/tf等工艺角仿真前一定要记得选不然可能默认没有加载任何section导致后面报model未定义的错误。在Analysis窗口里选择tran瞬态分析stop time设成200ns精度选moderate。为什么用transient而不是dc因为全加器是一个组合逻辑电路输入随时间变化只有tran仿真能直观看到从一组输入跳到另一组输入时输出的变化过程以及中间经历的传播延迟和毛刺。DC分析只能看某一种静态工作点没法完整验证真值表。4.2 三个输入激励怎么设计为了在一个tran仿真窗口中遍历8种输入组合最简单的方法是用周期成2倍关系的三个方波源。我这里给出我实测可用的参数信号V1V2PeriodPulse WidthRise/FallA01.820n10n0.1nB01.840n20n0.1nCin01.880n40n0.1n这里的思路是查看0到80ns的窗口A每20ns翻转一次B每40ns翻转一次Cin每80ns翻转一次三者频率成2倍关系于是窗口内的8个时间段刚好覆盖A、B、Cin的8种组合。波形不会严格按二进制000、001、010的次序排列但8种输入状态都会被跑到对照真值表验证逻辑完全足够。上升沿和下降沿千万别设成0。很多初学者犯这个错脉冲源的rise time写0结果Spectre在做tran分析时很难处理理想跳变轻则发出warning重则直接不收敛。给0.1ns的边沿时间对于180nm工艺的1.8V信号来说已经足够快完全不影响功能验证还能避免数值计算问题。如果想让波形更整洁可以改用vpwl分段线性源把每个时间点的电平精确列出比如0ns0、10ns1、20ns0这样一直写到80ns。vpwl的好处是控制精确坏处是参数多、写起来啰嗦。教学场景我用vpulse的频率倍数法已经够可靠。4.3 运行仿真与读取波形在ADE L中先关掉所有无关输出然后选择Outputs - To Be Plotted - Select On Schematic鼠标回到schematic窗口依次点选A、B、Cin、S、Cout这五根线选完后按Esc退出。点绿色Run按钮开始仿真。仿真结束后波形窗口会弹出这五条曲线。把波形放大看对照真值表逐段检查S和Cout的电平。比如在某一段A1、B0、Cin1时S应该是0、Cout应该是1。如果发现输出电平有问题先别急着改电路参数用Calculator里的Cross函数测一下延迟再确认数电逻辑是否对齐。延迟测量的标准做法是打开Calculator选择cross函数threshold设为0.9VVDD/2选中要测的信号比如从Cin的上升沿到S的上升沿读取两个cross时刻的差值。这个值就是路径上的传播延迟。电路规模不大时延迟通常只有几十到几百皮秒如果你看到几纳秒甚至几十纳秒就要怀疑管子尺寸是不是设得太保守或者负载电容加得过大。5. 高频踩坑器件未定义、不收敛与仿真发散5.1 器件未定义 / Model not foundERROR (ADE-3135): Model not found: nch这类报错基本可以锁定两类原因仿真模型库没有加载或者CDF参数里的Model name和模型库里定义的名字对不上。排查时先回ADE L的Model Libraries窗口确认已经加入了PDK的模型文件并且section选择正确然后打开底层晶体管symbol的属性看Model name是不是nch/pmch这类与库匹配的名字。如果PDK库的模型名是nmos1v8、pmos1v8而你的CDF里写成了nch那就改CDF参数或者在property里手动改成正确名字。这里还有一个细节当你建立了新库但没有attach技术库时调用的晶体管symbol可能来自某个默认analog库CDF参数里没有正确的model mapping也会导致model not found。所以在做任何电路之前确认三级关系工作库attach到PDK技术库技术库提供真实器件symbol器件CDF参数指向模型库里的模型名。5.2 瞬态仿真不收敛Spectre报Tran analysis: time step too small或者failed to converge是常见的坑。大多数情况下不是电路真的有问题而是仿真设置和激励源设置不够“友好”。我自己的排查顺序是这样的把脉冲源的rise time和fall time从0改成0.1ns或更大这一步能解决至少一半的不收敛问题。把tran分析的maxstep设小一点比如设为1ns也就是仿真窗口的1/200左右给Spectre一个更合理的步长边界。在ADE L的Simulation Options里加cmin1fF给每个节点一个非常小的寄生电容帮助Spectre稳定初值。如果还不行尝试把tran精度改成conservative代价是仿真更慢但收敛性更好。最后检查电路本身有没有悬空节点、有没有只接了一个PMOS或一个NMOS的浮空端、有没有窄脉冲竞争的循环路径。我在给本科生带实验时碰到过一种很典型的情况学生在testbench里给Cin加了一个vpwl上升沿和下降沿都填了0结果Spectre在边沿处反复重试步长最终报错。改完边沿时间后一次就通过了。所以这类问题绝大多数是仿真env问题不是设计问题。5.3 波形高低电平不对或延迟大到离谱如果波形出来了但高电平不到1.8V或者低电平高于0V基本是管子问题。先检查PMOS的bulk是不是接到了VDDNMOS的bulk是不是接到了GND。衬底没接对的情况下晶体管的体效应和漏电行为会异常输出会被拉到一个中间电平。如果高低电平正常但延迟大到离谱比如一个反相器延迟几十纳秒首先看管子的W/L是不是太小。L设成0.18u没问题但如果W也写成了0.18u那这个管子驱动能力极弱再加上负载电容延迟当然很大。把N管W调到1.2u以上P管按2到3倍比例设置延迟会立刻降下来。还有一个隐藏点确认你没有把Finger和Multiplier搞混。Finger是单管折叠Multiplier才是多管并联写错会直接影响等效宽长比。5.4 打开原理图库时报错这类问题更多和PDK安装、启动环境有关。比如启动Virtuoso后Library Manager里看不到PDK库或者某个cell的schematic打不开。先把启动日志完整看一遍确认.cdsinit和.cds.lib有没有报错。如果PDK是通过环境变量挂载的检查环境变量是否在当前shell里生效。有的PDK还需要把CDS_LOAD_ENV这类变量设置好。另一个常见的坑是文件权限共享服务器上PDK安装在别人的目录下如果没有读权限Virtuoso会报一堆奇怪的错误。找管理员确认权限别自己硬改文件权限容易出事。6. 从1-bit到更大设计一点扩展思考6.1 层次化复用的威力你已经把adder1bit做成了带symbol的cell再搭8-bit行波进位加法器只需要把8个adder1bit像叠积木一样串起来第一个的Cout接到第二个的Cin依次传递最后一个Cout就是整个8位加法的最高位进位。这个过程不需要再画任何晶体管也不需要再碰底层电路。Virtuoso的层次化设计在这里展现出真正的价值规模越大复用带来的效率提升越明显。这也是为什么真实芯片设计里很少有人在顶层原理图直接摆几百万个晶体管都是层层例化上去的。6.2 管级实现与抽象层级的对照在同一个设计里Verilog行为级、门级网表和晶体管级原理图描述的是同一个逻辑但信息量完全不同。行为级只告诉你S A ^ B ^ Cin门级网表告诉你用了几个门晶体管级原理图告诉你每个门的管子尺寸、衬底连接和负载效应。做纯数字前端停留在行为级就够了做定制电路、高性能运算单元或者IO电路就必须下到晶体管级。这次实验的意义就在于让你在仿真波形里直观看到抽象层级下降时新增的时间和电气信息。6.3 下一步是版图与后仿前仿真通过只是第一步真正要流片或者在PDK里继续推进后面还有版图设计、DRC、LVS、寄生参数提取和后仿真。180nm工艺虽然老朋友了但整个后端流程一点也不少你需要把每个晶体管画成有源区、多晶硅、金属层组成的版图跑设计规则检查再跑LVS确认版图与原理图一致最后提取寄生RC重新仿真。你会发现后仿真的波形比前仿真更有“锯齿感”延迟也更大因为导线电阻和电容都进来了。我做这个实验最大的体会是单纯在纸上推布尔表达式很容易但在Virtuoso里看到波形真真实实地按真值表跳变时你对CMOS电路逻辑的认知会变得非常具体。很多后面学数字后端或者模拟设计时理解的“延迟”“驱动能力”“负载效应”这些概念其实都可以在这个小全加器实验里提前获得直觉。建议你跑通之后试着改一改P管和N管的宽度比例再测一次延迟你会发现波形变化远比预想明显——这个工程直觉是看多少页书都换不来的。