简介:电路描述语言(CDL)教学演示文稿围绕C17基准电路,系统讲解如何用该语言描述电路结构、连接关系和逻辑功能,面向数字电路课程学习者、EDA入门者及需要完成电路描述实验报告的学生。内容先梳理语法规则,将语句分为函数连接语句、说明语句和继续语句三类,解释第一列空格、星号、等号的含义及标号长度限制;再重点讲解与、或、与非、或非、异或、非等逻辑门语句,以及扇出、输入、输出、电源、接地、结束等语句的书写格式。课件以C17电路为完整实例,结合十七个结点、五个原始输入、两个原始输出、三个扇出点和六个扇出分支,演示从输入管脚到输出管脚逐条编写的过程。同时介绍日本奈良大学用C语言编写的编译器,说明它如何将描述转换为电路内部结构表,包括元素表、前驱后继扩展表等要点。资源仅含一个PPT演示文稿文件,压缩包约一百九十六KB,便于下载后直接浏览;目前已有七百五十八人学习下载,适合作为课件补充或考前复习材料。
1. 电路描述语言CDL:一份能直接落地的网表语言
电路描述语言CDL,第一次见到它通常是一份后缀为.cdl的文本:里面是一段一段的.SUBCKT,写着M管、R、C和一堆节点名。它不是用来写逻辑的硬件描述语言,也不是仿真脚本,本职是把电路拓扑变成机器可以比较的文本——原理图导出一份,版图提取出一份,两份CDL放到一起做LVS一致性对比。后端验证、模拟设计、版图工程师都在跟它打交道。标题里带PPT,意思是这套内容可以直接铺成一次内部技术分享的讲稿:先看懂语法,再走提取流程,接着调参数,把高频坑过一遍,基本就能自己上手。
2. CDL语法骨架:从电路图到网表的映射规则
CDL可以看成Spice家族里的一个方言子集,但它比Spice仿真网表更“老实”:不需要声明复杂的器件模型参数,也通常不带寄生电阻电容,只保留连接关系与几何尺寸。原理图上的一个MOS管、一节导线、一个端口,在CDL里被映射成一行文本;整个电路的层级关系,被映射成子电路的嵌套。这个映射规则是所有后续工作的地基,看不懂它,后面LVS报的每一条错误都像天书。
2.1 节点、器件与子电路:CDL的三层结构
先看一个最小电路,CMOS反相器的CDL网表:
* CDL netlist for circuit INV1 .SUBCKT INV1 IN VDD VSS OUT M0 OUT IN VSS VSS NCH L=0.18U W=1.20U M=1 M1 OUT IN VDD VDD PCH L=0.18U W=2.40U M=1 .ENDS INV1第一行的*是注释符,供人读,不参与对比。.SUBCKT声明一个子电路,后面第一个字段是子电路名,从第二个字段开始就是端口列表:IN、VDD、VSS、OUT。端口顺序很重要,LVS做端口匹配时按位对应,四个端口的排列一变,对比结果就是另一回事。M0行是MOS管描述:第一个节点是漏端OUT,第二个是栅端IN,第三个是源端VSS,第四个是衬底VSS,再往后是模型名NCH和几何参数L、W、M。M1是P管,漏源栅衬底的顺序与M0一致,只是衬底接VDD。
这里的节点名不是坐标,而是电学网络名。写反了连接关系,CDL文本看起来合法,LVS也能跑起来,但对比结果全是错的。我见过有人把源漏对调后直接提交LVS,报错上千条,最后发现只是网表这一处笔误。M参数表示并联的器件数,M0的W=1.20U M=1意味着这个管就是单指,如果版图上画了四个并联等尺寸管,提取出来的CDL经常合并写成一个M=4的行,目的是减小对比规模。LVS时M值合并开与不开,直接影响对比粒度和后续仿真,这个坑放到第5章细说。
2.2 无源器件、控制语句与层级封装
CDL不只描述MOS管,带电阻电容的电路照样能写:
.SUBCKT RC_DRIVER IN OUT VDD VSS M0 OUT IN VSS VSS NCH L=0.18U W=1.20U M=1 M1 OUT IN VDD VDD PCH L=0.18U W=2.40U M=1 R0 OUT VDD 5.6K C0 OUT VSS 1.2P .ENDS RC_DRIVERR0是电阻实例,节点顺序是正端、负端、阻值,5.6K写成数字加单位后缀,提取工具和LVS工具都能解析。C0同理,OUT到VSS跨接一个1.2pF电容。无源器件的CDL写法比MOS管更宽松,因为没有衬底节点,也不一定有L/W信息,提取时更多的是靠版图层次识别结果回填数值。原理图导出的CDL里,电阻电容值是原理图符号上的标称值;版图提取的CDL里,这两个值是从几何图形算出来的。同一个电路,两份文件数值略有差异很正常,LVS对无源器件通常允许一定百分比容差,这个容差在对比规则里单独配。
除了器件行,还有几个控制语句要认。.PARAM用来定义参数化几何,比如.PARAM LMIN=0.18U,后面器件行就能写成L=LMIN,整个文件只改一处。.GLOBAL声明全局网络,最常见的写法是.GLOBAL VDD VSS,声明之后所有子电路里叫VDD的点都视为同一个电源网络,不需要在每个.SUBCKT里重复定义。这句话不写,有些工具会把不同层级的VDD当成两个孤立网络,直接导致电源连接错误。.ENDS必须有,任何一个.SUBCKT打开的括号都要闭合,漏写会让解析器把后面所有内容都吞进上一个子电路里。
层级封装是CDL里最容易被新人忽略的部分。一个真实的模拟IP不是扁平结构,顶层电路会引用多个子电路,子电路里还可能再套子电路。比如运放分成偏置电路、输入级、输出级,每一级都是一个.SUBCKT块,顶层只写实例引用。这种做法的直接好处是LVS支持hcell对比:完全相同的子电路只比对一次,剩下的是引用关系核对,速度和资源占用都大幅下降。反过来,如果网表全部摊平,几百个相同单元逐个对比,跑不动不说,错误报告也难读。所以导网表之前先确认顶层层级是否保留,这个开关通常在导出命令里,叫hierarchy或者flatten选项,具体叫法看环境。
再补一个容易踩的概念混淆。CDL不是仿真网表,它默认不带寄生参数,也没有温度、工艺角属性,直接拿去做spice仿真是缺材料的。很多仿真环境提供的“从版图提取后仿网表”,是在CDL基础上补齐了模型参数和寄生RC,而不是CDL本身。把两者分开记,能省去不少拍脑袋查错的时间。
3. 从版图和原理图产出CDL:提取流程与最小命令
有了语法基础,接下来要解决最实际的问题:两份CDL从哪里来。原理图侧通常由设计环境直接导出,只需要点导出命令选格式;版图侧则需要经历“导入版图数据、识别器件、提取连接、写出网表”四个阶段。流程里的命令名称和参数在不同环境差异很大,但工作逻辑一致,下面的命令都是示意写法,实际使用时以你手上的工具命令为准。整个流程走顺之后,LVS对比才有意义。
3.1 原理图网表导出:先解决端口一致性
原理图导CDL是相对简单的动作,常见的导出命令长得像这样:
# 导出原理图网表(示意命令,实际以当前环境命令为准) export_netlist -lib analog_lib -cell INV1 -view schematic \ -format cdl -out ./inv1_sch.cdl \ -hierarchy on -preserve_case yes -bus_bracket <>-lib、-cell、-view三个参数定位到具体的原理图对象;-format cdl指定输出格式;-out写输出路径;-hierarchy on保留子电路层级,尽量别关;-preserve_case yes让大小写保持原样;-bus_bracket <>规定总线符号用什么括号。这些参数里最容易埋雷的是大小写和总线括号。某些环境默认会把节点名统一成大写,或者把总线从<7:0>改成[7:0],一次导出后整个文件风格改变。如果版图侧提取时用的参数和这里一致,问题不大;不一致,LVS就会报出大量看起来莫名其妙的net不匹配。
导完第一件事不是直接拿去跑LVS,而是打开文件看头部。一个合格的反相器CDL,应该能看到.SUBCKT INV1 IN VDD VSS OUT,端口顺序和你预期的原理图引脚顺序一致。我习惯顺手数一遍器件行数量,和原理图里器件个数对照,对不上就先查导出环节,别把错误带到下一步。端口顺序在原理图符号和网表之间经常出现“视图不同导致顺序翻转”的问题:符号左边画输入、右边画输出,但正文里的端口列表如果按引脚创建顺序排列,VDD可能排在IN前面。LVS对比时端口按位匹配,顺序不一致就是满屏的connectivity error。
3.2 版图提取与LVS最小流程:从GDS到.cdl
版图侧提取CDL是重头戏,示意流程如下:
# 版图提取CDL(示意命令,实际以物理验证平台为准) extract -design ./inv1.gds -topcell INV1 \ -layermap ./rule/layermap.map \ -devicemap ./rule/device.map \ -out ./inv1_lay.cdl \ -mode LVS -extract_rc no-design指定版图数据源,GDS是常见格式;-topcell指定顶层单元名;-layermap把GDS里的层号映射成工艺层名;-devicemap决定哪些几何图形组合会被识别成MOS管、电阻、电容;-mode LVS表示只提取连接拓扑和器件尺寸,不输出寄生参数;-extract_rc no顺手关掉RC提取,跑LVS对比时不需要这些寄生值,开着只会拖慢速度。提取出来的文件就是inv1_lay.cdl。
拿到两份文件,接着跑LVS对比:
# LVS对比(示意命令) lvs -sch ./inv1_sch.cdl -lay ./inv1_lay.cdl \ -rule ./rule/lvs_rule.rules \ -out ./lvs_result.txt对比结果的报告文件里,第一优先看两个数字:connectivity errors和property errors。前者说明连接关系不一致,器件连错、端口对不上、net名漂移都会归到这里;后者说明连接一致但尺寸或参数数值超出容差,比如W、L、M值对不上。连接错误全部清零之后,再看property错误。很多新人看到connectivity errors为零就宣布通过,结果忽略了一排property errors,最后流片回来测出的电流和仿真差一大截,回头查才发现是版图里某个管子的W比原理图小了0.02微米,LVS报告里写得清清楚楚。
整个提取流程依赖一张完整的文件清单,缺一个都跑不起来。常见的最小集合包括:版图数据文件、层映射表、器件映射表、LVS规则文件。层映射表解决“版图层号与工艺层名的对应”,器件映射表解决“什么图形组合算一个器件”,LVS规则文件里则写了电源地名称、对比容差、合并选项。这三类文件通常由工艺库提供,后端工程师只是调用,但拿到手先确认版本配套。工艺库更新后沿用旧映射表,是最常见的一类“提不出来”事故,原因不是命令错了,而是表没跟上工艺版本。
4. CDL参数怎么设:命名、位序、映射与合并选项
提取和对比过程中的几个关键参数,决定LVS能不能一次通过,也决定通过之后的结果是否可信。参数表看起来零散,背后其实是同一个问题:两份网表要用同一种语言说话。命名规则不一致,对比时名字对不上;位序不一致,总线内部张冠李戴;器件映射不一致,同一个图形在这边是NMOS、在那边是空图形;合并选项不一致,拓扑看着一样但细节丢了。下面按影响面从大到小逐个说。
4.1 命名规则:大小写、特殊字符与总线位序的连锁反应
CDL里的节点名就是文本串,LVS对比本质上靠文本串做连接关系的等式匹配。等式匹配严格到什么程度,取决于参数。大小写是最先翻车的地方。有的环境导出的网表统一把端口名转成大写,VDD还是VDD,但一个叫NetA的节点变成NETA,原理图侧保持大小写混合,两边立刻对不上。更隐蔽的是特殊字符,节点名里的点号、斜杠、反引号在不同解析器眼里含义不同。比如用层次路径当节点名时,A/B/C在某些工具里被解释为路径分隔,另一些工具当成普通字符,提取结果完全两样。`.
所以导出前把preserve_case明确设为yes,并且规定整个项目统一用字母数字和下划线命名节点,不引入特殊符号,能从源头消除一批错误。总线位序的规则稍微复杂,同一根总线两种写法可能指向完全不同的一束线:
d<7:0> * 高位在前,d7是最高位 d[0:7] * 低位在前,d0配到低位bus_bracket参数决定用尖括号还是方括号,位序声明决定MSB和LSB的排布方向。原理图侧和版图侧如果用了不同的约定,提取出来的网表里同一根总线会被拆成两套名字,LVS报错时你会看到一堆类似d[7]和d<7>的提示,看着像同一条线,实际是两个不同文本串。解决方式也很机械:项目开始前定一个命名规范文档,导出命令里的括号和位序选项全部按规范填,不靠个人喜好。
4.2 器件映射与合并选项:直接决定对比粒度
器件映射和合并参数是LVS结果可信度的关键,列举一组最常用的参数及影响:
| 参数项 | 常见选项 | 主要影响 | 推荐做法 |
|---|---|---|---|
| preserve_case | yes / no | 节点大小写是否保留 | 固定yes,统一规范 |
| bus_bracket | < > / [ ] | 总线括号解析 | 与项目规范一致 |
| hierarchy / flatten | on / off | 子电路层级是否保留 | 对比用on,提速靠hcell |
| merge_by_M | on / off | 并联器件是否合并成M行 | LVS用on,后仿校准用off |
| extract_rc | no / r+c | 是否提取寄生RC | LVS对比用no |
| power_nets | VDD / AVDD 等 | 识别全局电源地 | 按工艺库定义填写 |
| device_tolerance | 百分比 | 无源器件容差范围 | 按流片方差留余量 |
merge_by_M值得单独说明。开启后,四个完全同尺寸的并联管会被合并成一行M=4,LVS对比速度快、报告简短,适合日常快速检查。但合并操作会丢失每个管子的独立实例信息,如果你要做后仿校准或者分析电流失配,必须关掉这个选项,保留四条独立器件行。我一般做法是:LVS快速验证阶段开M合并,进入后仿和可靠性检查阶段关掉重跑一遍,两份报告都看。extract_rc在LVS时务必关掉,否则提取器会把大量寄生电阻写进网表,LVS对比对象从“纯拓扑”变成“带寄生的拓扑”,对比结果容易被无谓的数值偏差干扰。等LVS拓扑确认无误,再单独跑一次带RC的提取,供后仿使用。
hcell相关选项也归到这一组。开启hierarchy后,多个相同子电路只对比一次,LVS报告里会写“compared as hcell”,速度提升明显。要注意hcell对比成功的前提是子电路内部完全一致:器件尺寸、器件类型、内部net名全部一致。只要有一个管子的尺寸偏了0.01微米,该子电路就会退出hcell优化,被展开成独立电路重新对比。排查LVS速度突然变慢时,先查报告里hcell命中率,命中率低的通常是某个子电路被改动过却没同步到另一侧。
5. CDL使用中的避坑指南:五条高频踩坑记录
CDL流程跑得越多,越发现大部分报错不是逻辑难题,而是约定不一致和文件版本错位。下面五条是实际工作中反复出现的情况,按现象、原因、解决三段记录,给同样被这类问题卡住的人一个排查顺序。
5.1 端口顺序被翻转,LVS报出一整片connection error
现象:原理图侧和版图侧都顺利导出了CDL,LVS跑完,connectivity errors上千条,报告里几乎每个net都标着不匹配。单独看原理图网表和版图网表都“没错”,可放一起就是全错。
原因:端口顺序不一致,最常见的版本是原理图符号按照引脚创建顺序导出端口,而版图顶层单元的端口顺序按物理位置排列,两个顺序不同,LVS按位匹配时第一对就错位,后面全部连锁反应。
解决:解析两份CDL的.SUBCKT行,人工核对端口列表。发现顺序不同,在导出命令里指定端口顺序,或者用文本工具调整其中一个文件,把端口排成一致再跑。习惯性做法是在LVS规则里启用“端口按名字匹配”而不是按位置匹配,能彻底免疫这类问题。
5.2 M值合并让LVS通过,后仿电流却对不上
现象:LVS报告通过,connectivity和property都是零错误。拿提取网表去做后仿,电流和预期差了百分之二三十,怎么检查电路图都找不出原因。
原因:提取时把四个并联管合并成了M=4,LVS对比逻辑认为这没问题;但后仿环境对待M=4的器件模型与四个独立器件实例存在数值差异,尤其是匹配敏感结构和靠近边界的器件,这种差异会被放大。
解决:后仿前用merge_by_M off重新提取一版CDL,确认四条独立器件行都在,再转成仿真网表。LVS通过只代表拓扑一致,不代表后仿网表保留了足够的细节,两件事要分开验证。
5.3 电阻被识别成孤立图形,报unmapped device
现象:版图提取的CDL里电阻缺失,LVS报告提示存在无法识别的图形。版图上电阻区域看得很清楚,提取器就是不认。
原因:器件映射表里电阻识别条件没有配全。常见的是少配了阻值层或者传感器层,例如扩散电阻缺少silicide block层标记,提取器不知道这段图形是电阻,直接当成普通连接图形处理。
解决:翻器件映射表,对照工艺文档补齐电阻的识别条件和阻值计算层。改完重新提取前,先跑一个小规模测试单元确认电阻被正确识别,再整芯片提取,避免反复跑大版图浪费时间。
5.4 版图看着没搭上,LVS却报short
现象:版图上两条走线隔着空隙,视觉上没有任何接触,LVS却报这两个net short。放大到局部层看,还是找不到连接点。
原因:连接层的定义比视觉更宽。某些情况下,同一层金属图形确实没有直接相交,但通孔层提取时把上下两层金属的接触区域判定为连接,而这两层金属分属不同net;更多的case是layer map里把两层不该合并的层映射成了同一个工艺层名,提取器自然认为它们是一体的。
解决:先查层映射表,确认所有涉及连接和通孔的层都一一对应,没有误合并。再查LVS规则里的连接层定义,确认哪些层被视为“连接层”。改完以后重新提取并跑一次小范围对比,确认short消失再上全芯片。
5.5 层映射表没更新,整个cell被当成空器件
现象:版图数据更新到新版工艺后,提取出来的CDL里部分cell器件数为零,或者器件的L、W数值异常,LVS报告显示一堆尺寸错误。
原因:层映射表还停留在旧版工艺。新版工艺改了层次划分,比如新增了某层做注入阻挡,旧映射表不认识这个新层号,导致器件识别区域被错误裁剪,出现“该识别成管的图形没识别到”。
解决:从工艺库重新获取当前版本的层映射表和器件映射表,替换旧文件后重跑提取。排查时先看提取日志里的warning,凡是提示“layer number not mapped”都要处理,不能忽略着往下跑。
6. 把CDL用到更深处:脚本体检与一份可复用的讲解稿
流程跑顺之后,可以做一点自动化来兜底。这里给一个极简的CDL体检脚本,用来快速核对两份网表的端口集合和器件数量,适合在每次LVS前当“冒烟测试”跑一遍:
# check_cdl_diff.py 用法: # python check_cdl_diff.py inv1_sch.cdl inv1_lay.cdl import sys, re def parse(path): ports, devices = [], [] in_sub = False for line in open(path, errors='ignore'): line = line.split('*')[0].strip() if line.startswith('.SUBCKT'): in_sub = True ports = line.split()[1:] continue if line.startswith('.ENDS'): in_sub = False continue if in_sub and line: dev = line.split()[0] if re.match(r'^[MCRLQD]$', dev): devices.append(dev) return set(ports), devices p1, d1 = parse(sys.argv[1]) p2, d2 = parse(sys.argv[2]) print('端口差异-仅原理图侧:', p1 - p2) print('端口差异-仅版图侧:', p2 - p1) print('原理图器件数:', len(d1), '版图器件数:', len(d2))parse函数按行处理,遇到.开头的行先拆掉注释再判断;.SUBCKT行取第一个字段后面的所有内容作为端口集合;器件行只认M、C、R、L、Q、D这几种首字母。脚本不能替代正式LVS,但能在几秒内发现端口或数量的明显偏差,省去直接跑大LVS排错的等待。参数就是两个CDL文件路径,顺序无所谓。
标题既然带着PPT,最后给一份可以直接铺成讲稿的内容分配建议:语法部分占两成,挑一个.SUBCKT当场拆解;提取流程占四成,最值得现场演示的是“同一份版图,开和关merge_by_M两次提取结果的差异”;参数设置占两成,重点讲命名规则和位序;高频坑占两成,挑本笔记里端口翻转和M值合并两条讲,听众最有共鸣。每讲完一节就放一次真实报告截图,比纯讲概念有效得多。
我做这件事早期吃过一个亏:原理图网表和版图网表一起报错上千条,排查了一下午才发现是电源地端口顺序颠倒了。那次之后养成了习惯,拿到任何CDL先看.SUBCKT行,再数器件个数,最后才跑对比。这个三步检查法至今受用。希望帮到你。
本文还有配套的精品资源,点击获取