3、设计输入:理解 LEF/DEF 格式。准备你的第一个设计文件。用 OpenROAD 读取设计。

好,我们正式开始动手了。前面两章我们把 OpenROAD 装好、跑通了 hello-world,但说实话,那只是个空壳子。真正的芯片设计,你得先搞清楚输入文件长什么样。

这一章,我们就来啃 LEF 和 DEF 这两个格式。别怕,它们没那么玄乎。说白了,LEF 是告诉你「库里有啥零件」,DEF 是告诉你「这些零件怎么摆、怎么连」。我当年刚入行时,对着这两个文件看了三天才搞明白谁是谁……嗯,今天争取让你三十分钟就理清楚。

3.1 LEF 和 DEF 到底在描述什么?

先打个比方。你要盖一栋楼:

  • LEF(Library Exchange Format) —— 相当于建材目录。里面写着每种砖头、每根钢筋的尺寸、引脚位置、不能走线的区域( obstruction )。它不关心你这栋楼具体长啥样,只告诉你「我有这些标准件」。
  • DEF(Design Exchange Format) —— 相当于施工图纸。它说:哪块砖放在哪个坐标,哪根线连到哪个引脚,电源网络怎么走。它不关心砖头内部长啥样,只关心位置和连接关系。

在 OpenROAD 流程里,这两兄弟缺一不可。LEF 提供物理库信息,DEF 提供设计实例化信息。你想想看,没有 LEF,工具不知道每个单元多大;没有 DEF,工具不知道你要放什么。所以第一步,就是让 OpenROAD 把这两个文件吃进去。

核心概念速记:

  • LEF = 库视图(技术 + 宏单元)
  • DEF = 设计视图(实例 + 连线 + 布局)
  • OpenROAD 读 LEF 在前,读 DEF 在后

3.2 LEF 文件结构速览

LEF 文件分两大部分:技术 LEF 和 单元 LEF。技术 LEF 定义工艺信息,比如金属层数、最小线宽、通孔规则。单元 LEF 定义每个标准单元或宏单元的物理轮廓和引脚位置。

我截一段真实的 LEF 片段给你看,别被吓到,我们只关注关键字段:

MACRO AND2X1
  CLASS CORE ;
  ORIGIN 0 0 ;
  SIZE 0.56 BY 2.72 ;
  PIN A
    DIRECTION INPUT ;
    PORT
      LAYER M1 ;
      RECT 0.14 1.22 0.28 1.50 ;
    END
  END A
  PIN Y
    DIRECTION OUTPUT ;
    PORT
      LAYER M1 ;
      RECT 0.28 1.22 0.42 1.50 ;
    END
  END Y
  OBS
    LAYER M1 ;
    RECT 0 0 0.56 2.72 ;
  END
END AND2X1

看到没?一个 AND2X1 单元,宽 0.56 微米,高 2.72 微米。两个引脚 A 和 Y,都在 M1 层上,坐标范围清清楚楚。OBS 区域告诉工具:这整个矩形内部,其他单元不能占用。

我在项目中遇到过最坑的事,就是某个第三方 IP 的 LEF 里 OBS 画错了,导致 APR 时工具死活绕不开那个区域。后来手动查 LEF 才发现,OBS 比实际芯片大了 10 倍……所以,拿到 LEF 后,我建议你先用文本编辑器扫一眼 OBS 和 PIN 的坐标,心里有个底。

3.3 DEF 文件结构速览

DEF 描述的是你的具体设计。它包含:

  • DIEAREA —— 芯片的边界
  • COMPONENTS —— 所有实例化的单元
  • PINS —— 芯片的顶层端口
  • NETS —— 互连关系
  • SPECIALNETS —— 电源/地网络

一个简单的 DEF 片段长这样:

DESIGN top ;
UNITS DISTANCE MICRONS 1000 ;
DIEAREA ( 0 0 ) ( 100 100 ) ;
COMPONENTS 3 ;
  - u1 AND2X1 + PLACED ( 10 20 ) N ;
  - u2 INVX1 + PLACED ( 30 40 ) N ;
  - u3 DFFX1 + PLACED ( 50 60 ) N ;
END COMPONENTS
PINS 2 ;
  - clk + NET clk + DIRECTION INPUT + USE SIGNAL
    + LAYER M1 ( 0 0 ) ( 5 5 ) + PLACED ( 0 50 ) N ;
  - out + NET out + DIRECTION OUTPUT + USE SIGNAL
    + LAYER M1 ( 0 0 ) ( 5 5 ) + PLACED ( 100 50 ) N ;
END PINS
NETS 3 ;
  - net1 ( u1 Y ) ( u2 A ) ;
  - net2 ( u2 Y ) ( u3 D ) ;
  - net3 ( clk ) ( u3 CK ) ;
END NETS
END DESIGN

这个设计只有 3 个单元、2 个端口、3 条网线。当然真实设计不会这么简单,但结构一模一样。DEF 的 COMPONENTS 段里,+ PLACED 表示已经摆好了位置;如果是 + UNPLACED,那就是还没布局,等着工具去摆。

小技巧: 刚开始学的时候,你可以用 grep 命令快速统计 DEF 里的单元数量:grep -c "PLACED\|UNPLACED" your.design.def。这样一眼就知道设计规模有多大。

3.4 准备你的第一个设计文件

我们不用真实的大设计,那太复杂了。我准备了一个极简的「计数器」设计,只有 5 个标准单元。你跟着我做:

  1. 创建一个工作目录,比如 ~/openroad_lab/ch03
  2. 下载或复制 Nangate 45nm 的 LEF 文件(技术 LEF + 单元 LEF)
  3. 用文本编辑器写一个简单的 DEF 文件,包含 5 个单元和它们的连线

这里我直接给你一个能用的 DEF 模板。你把它保存为 counter.def:

DESIGN counter ;
UNITS DISTANCE MICRONS 1000 ;
DIEAREA ( 0 0 ) ( 50 50 ) ;
COMPONENTS 5 ;
  - u0 DFFX1 + UNPLACED ;
  - u1 DFFX1 + UNPLACED ;
  - u2 AND2X1 + UNPLACED ;
  - u3 INVX1 + UNPLACED ;
  - u4 BUFX2 + UNPLACED ;
END COMPONENTS
PINS 3 ;
  - clk + NET clk + DIRECTION INPUT + USE SIGNAL
    + LAYER M1 ( 0 0 ) ( 2 2 ) + PLACED ( 0 25 ) N ;
  - rst + NET rst + DIRECTION INPUT + USE SIGNAL
    + LAYER M1 ( 0 0 ) ( 2 2 ) + PLACED ( 0 30 ) N ;
  - count_out + NET count_out + DIRECTION OUTPUT + USE SIGNAL
    + LAYER M1 ( 0 0 ) ( 2 2 ) + PLACED ( 50 25 ) N ;
END PINS
NETS 4 ;
  - clk_net ( clk ) ( u0 CK ) ( u1 CK ) ;
  - rst_net ( rst ) ( u0 R ) ( u1 R ) ;
  - q0_to_d1 ( u0 Q ) ( u1 D ) ;
  - count_net ( u1 Q ) ( u2 A ) ( u3 A ) ( u4 A ) ;
END NETS
END DESIGN

注意,这里所有单元都是 UNPLACED,因为我们还没做布局。端口 clk 和 rst 放在了芯片左边界,count_out 放在右边界。网表连接也很简单:两个 DFF 级联,输出经过一些组合逻辑到端口。

警告: 你的 LEF 文件里必须有 DFFX1、AND2X1、INVX1、BUFX2 这四个单元的定义,否则 OpenROAD 读 DEF 时会报错说找不到单元。我建议你直接用 Nangate 45nm 的开放库,它包含这些基本单元。

3.5 用 OpenROAD 读取设计

文件准备好了,现在让 OpenROAD 把它们读进来。启动 OpenROAD 后,在 Tcl 命令行里依次执行:

# 第一步:读入技术 LEF(工艺信息)
read_lef /path/to/Nangate45/NangateOpenCellLibrary.lef

# 第二步:读入单元 LEF(标准单元物理信息)
# 注意:如果技术 LEF 和单元 LEF 是分开的,需要分别读
# 这里假设它们已经合并到一个文件里了

# 第三步:读入 DEF 设计文件
read_def counter.def

# 第四步:检查读入结果
report_components
report_nets
report_pins

如果一切顺利,你会看到类似这样的输出:

Components: 5
  u0 (DFFX1)
  u1 (DFFX1)
  u2 (AND2X1)
  u3 (INVX1)
  u4 (BUFX2)
Nets: 4
  clk_net (3 pins)
  rst_net (3 pins)
  q0_to_d1 (2 pins)
  count_net (4 pins)
Pins: 3
  clk (INPUT)
  rst (INPUT)
  count_out (OUTPUT)

看到这个,就说明你的设计已经被 OpenROAD 成功加载了。所有单元、网线、端口都在内存里了,随时可以开始布局布线。

我记得第一次成功读入自己的 DEF 时,那种感觉就像……嗯,就像你拼好了一个乐高模型的第一个零件。虽然还只是个骨架,但至少证明你的「图纸」没画错。

3.6 常见问题与避坑

读文件时最容易遇到这几个问题,我帮你列出来:

错误信息 原因 解决办法
Can't find macro 'XXX' DEF 里引用了 LEF 中没有的单元 检查 LEF 文件是否包含该宏单元,或 DEF 里单元名拼写是否正确
LEF pin layer not found DEF 中引用的金属层在 LEF 技术文件中未定义 确保技术 LEF 包含了所有用到的金属层定义
DEF units mismatch DEF 和 LEF 的单位不一致 检查 DEF 头部 UNITS DISTANCE MICRONS 的值,通常为 1000 或 2000
Read DEF failed: syntax error DEF 文件格式有误,比如少写了分号或括号不匹配 用文本编辑器检查 DEF 语法,特别注意 END 和分号

我的习惯: 每次写 DEF 后,我都会先用 defcheck 工具(OpenROAD 自带)做一次语法检查:defcheck counter.def。它能帮你提前发现 90% 的格式问题,省得在 OpenROAD 里报错再回头改。

3.7 本章知识结构图

下面这张图帮你把 LEF、DEF 和 OpenROAD 的关系理清楚:

LEF/DEF 与 OpenROAD 关系图 LEF / DEF 与 OpenROAD 读取流程 技术 LEF 工艺信息、金属层、规则 单元 LEF 标准单元、宏单元物理信息 DEF 设计文件 实例、网线、端口、布局 OpenROAD read_lef + read_def read_lef read_def 设计就绪,可进行 APR 注意 先读 LEF,再读 DEF 顺序错了会报错

从这张图可以看得很清楚:LEF 提供「零件手册」,DEF 提供「装配图」,OpenROAD 把两者合并后,你的设计才真正在工具里「活」起来。后续的 floorplan、placement、routing,全都建立在这个基础上。

3.8 本章小结

这一章我们做了三件事:

  • 理解了 LEF 和 DEF 各自描述什么,以及它们的关系
  • 亲手写了一个极简的 DEF 文件,包含 5 个单元和 4 条网线
  • 用 OpenROAD 的 read_lef 和 read_def 命令成功加载了设计

说实话,LEF/DEF 的细节远不止这些,但作为入门,你掌握到这个程度已经够了。后面几章我们会在这个小设计上一步步做布局、布线、时序分析。到时候你会发现,今天花时间搞懂这两个格式,值。