3. 设计输入与准备:RTL 文件组织、Liberty/LEF/DEF 文件格式说明、SDC 约束文件准备、设计目录结构规范
好了,咱们正式开始动手前的最后一步——把“食材”备齐。做芯片后端设计,说白了就是一场“输入-处理-输出”的游戏。输入文件的质量,直接决定了你后面几个星期甚至几个月是顺风顺水,还是天天加班改脚本。
我个人习惯,在跑任何一条 OpenROAD 流程之前,都会花半天时间把输入文件理一遍。别嫌麻烦,这步省下来的时间,后面会加倍还给你。
3.1 RTL 文件组织:别让代码“跑”丢了
RTL 是设计的源头。很多新手喜欢把所有 .v 文件扔到一个目录里,结果到了综合阶段,读文件顺序错了,或者漏了一个模块,整个流程就崩了。
我建议的目录结构:
rtl/
├── src/ # 设计源文件
│ ├── core/ # 核心逻辑模块
│ ├── peri/ # 外设接口模块
│ └── top/ # 顶层模块
├── tb/ # 测试激励(后端一般不用,但保留)
├── filelist.f # 文件列表(核心!)
└── include/ # 头文件/宏定义
为什么要用 filelist.f?因为 OpenROAD 的 read_verilog 命令支持直接读文件列表。你只需要维护一个文本文件,按依赖顺序把 .v 文件列好就行。
filelist.f 示例:
# 核心模块
./src/core/alu.v
./src/core/regfile.v
./src/core/ctrl.v
# 外设模块
./src/peri/uart.v
./src/peri/spi.v
# 顶层
./src/top/chip_top.v
# 开头,OpenROAD 会自动忽略。
3.2 Liberty 文件:芯片的“速度与功耗”说明书
Liberty(.lib)文件,说白了就是标准单元库的“数据手册”。它告诉工具:每个门有多快、功耗多大、输入电容是多少。
一个典型的 .lib 文件包含:
| 字段 | 含义 | 我的经验 |
|---|---|---|
cell |
单元名称,如 AND2X1 | 命名规则一定要统一,不然后面查库很痛苦 |
pin |
引脚定义,包括方向、电容 | 注意检查输出引脚的 max_transition 限制 |
timing |
时序弧,包括 rise/fall 延迟 | 不同 PVT 条件下的表格要完整,缺了会报错 |
power |
内部功耗和泄漏功耗 | 泄漏功耗在先进工艺下越来越重要 |
在 OpenROAD 里,读 Liberty 的命令很简单:
read_liberty -max /path/to/slow.lib
read_liberty -min /path/to/fast.lib
3.3 LEF 文件:芯片的“物理蓝图”
LEF(Library Exchange Format)描述的是单元的物理尺寸、引脚位置、布线阻挡层等信息。它和 Liberty 是“双胞胎”——一个管时序功耗,一个管物理形状。
LEF 文件的核心内容:
- MACRO:定义每个标准单元或宏模块
- SIZE:单元的宽和高(单位通常是微米或纳米)
- PIN:引脚名称、层、坐标
- OBS:布线阻挡层,告诉工具哪些区域不能走线
OpenROAD 读 LEF 的命令:
read_lef /path/to/tech.lef # 工艺LEF(包含金属层定义、间距规则)
read_lef /path/to/cells.lef # 单元LEF(包含所有标准单元和IO)
3.4 DEF 文件:设计的“初始状态”
DEF(Design Exchange Format)描述的是整个芯片的物理信息——芯片大小、IO 位置、电源网络、以及初始的单元摆放和布线。
DEF 的主要用途:
- 初始 DEF:从顶层规划工具导出,包含芯片尺寸和 IO 位置
- 中间 DEF:在布局、时钟树综合、布线等步骤之间传递设计状态
- 最终 DEF:输出给物理验证和签核工具
在 OpenROAD 流程中,你通常会在读入 RTL 和库之后,再读入一个初始 DEF:
read_def /path/to/floorplan.def
这个 DEF 里一般只包含芯片边界和 IO 引脚的位置。后面的布局布线结果,OpenROAD 会自己写到新的 DEF 文件里。
3.5 SDC 约束文件:给芯片定“规矩”
SDC(Synopsys Design Constraints)是时序约束的核心。没有它,工具就不知道你的芯片要跑多快、哪些路径是假的、哪些时钟是异步的。
一个典型的 SDC 文件包含:
| 命令 | 作用 | 我常用的写法 |
|---|---|---|
create_clock |
定义时钟 | create_clock -name clk -period 10 [get_ports clk] |
set_input_delay |
输入路径约束 | set_input_delay -clock clk -max 5 [all_inputs] |
set_output_delay |
输出路径约束 | set_output_delay -clock clk -max 5 [all_outputs] |
set_false_path |
忽略某些路径 | set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b] |
clk 写成 CLK,结果工具报“clock not found”,查了半天才发现是大小写问题。
3.6 设计目录结构规范:让项目“井井有条”
最后,我分享一下我个人用了多年的目录结构。不管项目大小,这套结构都能让你快速找到需要的文件。
project/
├── rtl/ # RTL 源文件
│ ├── src/
│ └── filelist.f
├── lib/ # 库文件
│ ├── liberty/ # .lib 文件
│ ├── lef/ # .lef 文件
│ └── db/ # 工具生成的数据库(可选)
├── constraints/ # SDC 约束
│ └── chip.sdc
├── scripts/ # OpenROAD Tcl 脚本
│ ├── 1_synth.tcl
│ ├── 2_floorplan.tcl
│ ├── 3_place.tcl
│ ├── 4_cts.tcl
│ ├── 5_route.tcl
│ └── 6_finish.tcl
├── output/ # 输出文件
│ ├── synth/
│ ├── floorplan/
│ ├── place/
│ ├── cts/
│ ├── route/
│ └── gds/
└── logs/ # 日志文件
└── run.log
这套结构的好处是:每个阶段的输入输出都放在独立的子目录里,不会互相覆盖。而且日志单独放,方便回溯问题。
3.7 本章知识体系总览
下面这张图,把本章讲到的所有输入文件、它们之间的关系、以及它们在 OpenROAD 流程中的位置,做了一个完整的梳理。你可以把它当作一个“快速导航图”。
这张图把整个流程串起来了。你从左边准备好 RTL、Liberty、LEF、DEF 和 SDC,然后 OpenROAD 会依次经过综合、布局规划、布局、时钟树、布线,最后输出签核结果。每个阶段都会读入上一阶段的 DEF,并写出新的 DEF 给下一阶段。
嗯,到这里,设计输入与准备的内容就讲完了。把这些文件理清楚,你的 OpenROAD 流程就已经成功了一半。