3. 数据准备与库文件解析:LEF、DEF、Liberty、TLU+ 文件格式详解
做后端布局,说白了就是跟一堆库文件打交道。很多新手一上来就被 LEF、DEF、Liberty、TLU+ 这些缩写搞晕了。我刚开始接触时也差不多,对着文件夹里几十个 .lib、.lef 文件发愣——它们到底是干嘛的?
今天咱们就把这些文件一个一个拆开看。你搞懂了它们,后端流程就通了三分之一。
3.1 LEF 文件:物理库的“骨架”
LEF(Library Exchange Format)描述的是芯片的物理信息。它不关心电路怎么工作,只关心“长什么样、占多大地方”。
我个人习惯把 LEF 分成两类:
- 技术 LEF:定义工艺规则,比如金属层数、最小线宽、间距、通孔类型。这些是工艺厂给的,一般不能改。
- 单元 LEF:定义每个标准单元、宏单元的物理尺寸、端口位置、障碍层。比如一个 NAND2 门,LEF 里会写它宽 0.5μm、高 1.0μm,A/B 输入端口在左边,输出 Y 在右边。
关键点:LEF 只包含 外形 和 端口位置,不包含逻辑功能。功能在 Liberty 里。
举个例子,一个简单的 INV 单元 LEF 片段长这样:
MACRO INV
CLASS CORE ;
ORIGIN 0 0 ;
SIZE 0.5 BY 1.0 ;
PIN A
DIRECTION INPUT ;
PORT
LAYER M1 ;
RECT 0.1 0.4 0.3 0.6 ;
END
END A
PIN Y
DIRECTION OUTPUT ;
PORT
LAYER M1 ;
RECT 0.2 0.4 0.4 0.6 ;
END
END Y
OBS
LAYER M1 ;
RECT 0 0 0.5 1.0 ;
END
END INV
你看,它只说了这个单元占 0.5×1.0 的面积,A 和 Y 端口在 M1 层上的坐标,以及 M1 层哪些区域不能走线(OBS)。
我的经验:导入 LEF 时最容易踩的坑是 技术 LEF 和单元 LEF 版本不匹配。我曾经遇到过技术 LEF 定义了 8 层金属,但单元 LEF 里某个宏单元用了第 9 层——工具直接报错。所以导入前一定要检查层数一致性。
3.2 DEF 文件:设计的“蓝图”
DEF(Design Exchange Format)描述的是你的 具体设计。如果说 LEF 是乐高积木的说明书,那 DEF 就是你用这些积木搭出来的模型。
DEF 里包含:
- 芯片的尺寸(DIEAREA)
- 所有单元的位置(COMPONENTS)
- 端口的位置(PINS)
- 走线信息(NETS)
- 障碍、区域约束等
一个典型的 DEF 片段:
DESIGN my_chip ;
DIEAREA ( 0 0 ) ( 1000 1000 ) ;
COMPONENTS 3 ;
- U1 INV + PLACED ( 100 200 ) N ;
- U2 NAND2 + PLACED ( 300 200 ) N ;
- U3 DFF + PLACED ( 500 200 ) N ;
END COMPONENTS
PINS 4 ;
- clk + NET clk + DIRECTION INPUT + LAYER M3 ( 50 50 ) ;
- data + NET data + DIRECTION INPUT + LAYER M3 ( 50 100 ) ;
- out + NET out + DIRECTION OUTPUT + LAYER M3 ( 950 50 ) ;
END PINS
NETS 3 ;
- net1 ( U1 Y ) ( U2 A ) ;
- net2 ( U2 Y ) ( U3 D ) ;
- net3 ( clk ) ( U3 CK ) ;
END NETS
END DESIGN
DEF 是后端流程中 最常读写 的文件。布局完成后写 DEF,布线完成后写 DEF,ECO 修改后也写 DEF。我一般会养成习惯:每做完一个步骤就 dump 一份 DEF 出来,方便回溯。
注意:DEF 的坐标单位是 微米(μm) 还是 数据库单位(DBU),取决于你在 LEF 里定义的精度。我见过有人把单位搞混,结果单元飞到芯片外面去了。导入后第一件事:用工具画一下 floorplan,看看单元是不是在合理位置。
3.3 Liberty 文件:时序的“灵魂”
Liberty(.lib)文件描述的是每个单元的 时序和功耗特性。它告诉你:
- 输入到输出的延迟(cell rise/fall)
- 建立时间、保持时间(setup/hold)
- 输出驱动能力
- 输入电容
- 漏电功耗、动态功耗
这些数据是通过 查表(NLDM) 或 电流源模型(CCS) 给出的。查表维度通常是输入转换时间(slew)和输出负载电容(load)。
一个简化的 Liberty 条目:
cell (INV) {
area : 0.5 ;
pin (A) {
direction : input ;
capacitance : 0.002 ;
}
pin (Y) {
direction : output ;
function : "(!A)" ;
timing () {
related_pin : "A" ;
timing_type : combinational ;
cell_rise (delay_template_7x7) {
index_1 ("0.01, 0.1, 0.5, 1.0, 2.0, 4.0, 8.0") ;
index_2 ("0.01, 0.05, 0.1, 0.5, 1.0, 2.0, 4.0") ;
values ( \
"0.02, 0.04, 0.08, 0.15, 0.25, 0.40, 0.60", \
"0.03, 0.06, 0.10, 0.18, 0.30, 0.45, 0.70", \
...
) ;
}
}
}
}
这里 index_1 是输入 slew,index_2 是输出负载,values 是对应的延迟。工具通过 二维插值 算出实际工作条件下的延迟。
避坑指南:我曾经遇到一个项目,综合用的 .lib 是 slow 工艺角,后端导入的却是 fast 工艺角。结果时序分析全乱套了。所以导入 Liberty 时一定要确认 工艺角(PVT) 是否正确——温度、电压、工艺偏差这三个参数必须跟你的设计目标一致。
3.4 TLU+ 文件:互连线的“电阻电容”
TLU+(Technology LEF-based Unified parasitic format)是工艺厂提供的 寄生参数文件。它描述了每层金属的:
- 单位长度电阻(R per μm)
- 单位长度电容(C per μm)
- 层间耦合电容
- 通孔电阻
没有 TLU+,工具就不知道一根 100μm 长的 M2 走线到底有多大的 RC 延迟。说白了,TLU+ 是 时序精度的基石。
TLU+ 文件通常以 .tluplus 或 .itf 格式提供。导入时需要配合 映射文件(map file),把 LEF 里的层名映射到 TLU+ 里的层名。
举个例子:LEF 里叫 M1,TLU+ 里叫 METAL1。映射文件里写一行 M1 METAL1,工具才能对上号。
3.5 工艺库的导入与检查
好了,文件都认识了。那怎么导入?导入后怎么确认没问题?
我一般按这个流程走:
- 整理文件清单:确认拿到技术 LEF、单元 LEF、Liberty(所有工艺角)、TLU+、映射文件。
- 检查版本一致性:所有文件必须来自 同一工艺版本。混用不同版本的库是灾难。
- 导入工具:以 Innovus 为例,用
setTechLEF、setCellLEF、read_lib、set_tlu_plus命令依次导入。 - 运行检查:工具一般有
check_library或verify_library命令。跑一遍,看有没有 missing cell、missing pin、层名不匹配等问题。 - 可视化验证:打开 GUI,随机挑几个单元看看。形状对不对?端口位置合理吗?
下面这张图总结了四种文件的关系:
3.6 导入后的检查清单
导入不是点一下按钮就完事了。我每次都会过一遍这个清单:
| 检查项 | 方法 | 常见问题 |
|---|---|---|
| LEF 层数一致性 | 对比技术 LEF 和单元 LEF 的金属层定义 | 单元 LEF 用了未定义的层 |
| Liberty 工艺角 | 确认 .lib 文件名或内部 PVT 条件 | 误用不同电压的库 |
| TLU+ 映射 | 检查 map 文件是否覆盖所有 LEF 层 | 漏映射导致寄生提取失败 |
| 单元完整性 | 运行 check_library 或 report_library |
缺少某些标准单元 |
| 端口对齐 | GUI 中查看单元端口位置 | 端口在障碍区内 |
血的教训:我曾经在一个 28nm 项目里,导入 LEF 时没注意技术 LEF 里定义了 双重图案(DPT) 规则,但单元 LEF 没更新。结果布线时工具死活绕不过去,最后发现是 LEF 版本不匹配。从那以后,我每次导入都会用 diff 命令对比新旧 LEF 的层定义部分。
嗯,数据准备这一步看着琐碎,但真不能省。你想想看,如果库文件本身就有问题,后面做的所有工作都是白费。花半小时检查,能省后面好几天 debug 的时间。