3. 设计输入与准备:RTL 设计文件、Liberty 库文件、LEF/DEF 文件、SDC 约束文件,构建项目目录结构
好了,咱们正式开始动手。在做任何后端工作之前,有一件事必须搞定——设计输入文件的准备与项目目录的搭建。
这步看着简单,但我见过太多新手甚至老手在这上面翻车。文件放错位置、库版本不对、约束文件缺胳膊少腿……等到跑流程到一半报错,回头排查才发现是输入文件的问题,那叫一个痛苦。
我个人习惯,在项目启动的第一天,先把目录结构搭好,把所有输入文件归位。这就像做饭前先把菜洗好切好,后面炒起来才顺手。
3.1 我们需要哪些输入文件?
OpenROAD 的输入,说白了就是五样东西。我列个表,你对照着准备:
| 文件类型 | 扩展名 | 作用 | 来源 |
|---|---|---|---|
| RTL 设计文件 | .v / .sv | 描述电路功能逻辑 | 前端设计团队 |
| Liberty 库文件 | .lib | 标准单元时序与功耗信息 | 工艺厂商 / PDK |
| LEF 文件 | .lef | 标准单元物理尺寸与引脚位置 | 工艺厂商 / PDK |
| DEF 文件 | .def | 初始布局或顶层互连信息 | 前端 / 顶层集成 |
| SDC 约束文件 | .sdc | 时序、面积、功耗约束 | 前端设计团队 |
file 命令或文本编辑器打开看一眼。有时候前端给过来的 .v 文件其实是加密过的,或者 .lib 文件是旧版本的。看一眼,省得后面白跑。
3.2 RTL 设计文件
RTL 文件是芯片的「源代码」。OpenROAD 支持 Verilog 和 SystemVerilog。这里有个坑——OpenROAD 的 Yosys 综合器对 SystemVerilog 的支持有限。如果你用了复杂的 SV 语法(比如 interface、class),可能会报错。
我曾经接手过一个项目,前端给了满篇的 SV 语法,结果 Yosys 直接罢工。最后只能让前端把 RTL 改成纯 Verilog-2001 可综合子集。所以,保险起见,建议用 Verilog-2001 风格。
文件组织上,我习惯把所有 RTL 源文件放在 rtl/ 目录下,并维护一个 filelist.f 文件,列出所有源文件的路径。这样在综合时直接引用 filelist 就行,不用在命令行里写一长串。
# rtl/filelist.f 示例
./rtl/top.v
./rtl/cpu/core.v
./rtl/cpu/alu.v
./rtl/mem/ram.v
./rtl/mem/rom.v
./rtl/periph/uart.v
./rtl/periph/spi.v
3.3 Liberty 库文件
Liberty 文件(.lib)是标准单元的「时序身份证」。里面记录了每个单元的延迟、功耗、建立时间、保持时间等参数。OpenROAD 在综合和时序分析时都要用到它。
这里要注意:Liberty 文件有不同的工作条件版本。通常工艺厂商会提供多个 corner 的 .lib 文件:
- 典型条件(typical):用于常规分析
- 最差条件(worst):用于 setup 检查
- 最好条件(best):用于 hold 检查
我个人习惯,在项目目录下建一个 lib/ 文件夹,里面按 corner 再分子目录:
lib/
├── typical/
│ └── sky130_fd_sc_hd__tt_025C_1v80.lib
├── worst/
│ └── sky130_fd_sc_hd__ss_100C_1v60.lib
└── best/
└── sky130_fd_sc_hd__ff_125C_1v95.lib
3.4 LEF 与 DEF 文件
LEF 文件描述的是标准单元的物理信息——每个单元占多大面积、引脚在什么位置、有哪些布线阻挡层。DEF 文件则描述的是芯片的布局信息——单元放在哪里、模块之间的连接关系。
对于 OpenROAD 流程:
- LEF 文件:综合和布局布线阶段都需要。通常包含技术 LEF(描述金属层、通孔规则)和单元 LEF(描述标准单元)。
- DEF 文件:如果你是从前端拿到一个初始布局,或者要做顶层集成,就需要读入 DEF。如果是纯平摊设计,可以从零开始,不需要初始 DEF。
我建议把 LEF 文件放在 lef/ 目录,DEF 文件放在 def/ 目录。如果项目有多个 DEF 版本(比如 floorplan 阶段、placement 阶段),可以加时间戳或版本号区分。
lef/
├── sky130_fd_sc_hd__tech.lef
└── sky130_fd_sc_hd__cells.lef
def/
├── top_floorplan_v1.def
└── top_floorplan_v2.def
3.5 SDC 约束文件
SDC 文件是后端流程的「指挥棒」。它告诉工具:时钟频率是多少、哪些路径是假的、哪些信号需要特别处理。
一个典型的 SDC 文件长这样:
# 创建时钟
create_clock -name clk -period 10.0 [get_ports clk]
# 设置输入延迟
set_input_delay -clock clk -max 2.0 [get_ports data_in]
set_input_delay -clock clk -min 1.0 [get_ports data_in]
# 设置输出延迟
set_output_delay -clock clk -max 3.0 [get_ports data_out]
set_output_delay -clock clk -min 1.5 [get_ports data_out]
# 设置伪路径
set_false_path -from [get_clocks rst_n]
# 设置最大扇出
set_max_fanout 20 [current_design]
这里有个经验:SDC 文件的质量直接决定后端流程的成败。我见过太多项目因为 SDC 里漏了某个时钟约束,导致综合出来的网表时序完全不对。
read_sdc 命令加载,然后用 report_checks 看看工具是否成功解析了所有约束。我曾经遇到过一个 SDC 文件里用了 get_pins 但实际设计中根本没有那个 pin 名,结果工具默默忽略了这个约束,导致后面时序分析全错。
3.6 构建项目目录结构
好了,文件都介绍完了。现在咱们来搭目录结构。我有一套自己用了多年的模板,分享给你:
project_name/
├── rtl/ # RTL 源文件
│ ├── filelist.f # 文件列表
│ ├── top.v
│ ├── cpu/
│ └── mem/
├── lib/ # Liberty 库文件
│ ├── typical/
│ ├── worst/
│ └── best/
├── lef/ # LEF 文件
├── def/ # DEF 文件
├── sdc/ # SDC 约束文件
│ └── top.sdc
├── scripts/ # Tcl 脚本
│ ├── synth.tcl
│ ├── floorplan.tcl
│ ├── place.tcl
│ ├── cts.tcl
│ ├── route.tcl
│ └── sta.tcl
├── output/ # 输出文件
│ ├── netlist/
│ ├── layout/
│ └── reports/
└── README.md # 项目说明
这个结构的好处是:
- 输入输出分离:源文件在
rtl/、lib/等目录,生成的文件在output/,不会混在一起 - 脚本独立:所有 Tcl 脚本放在
scripts/,方便管理和版本控制 - 层次清晰:每个阶段都有对应的脚本,想重跑哪一步直接执行对应的 .tcl 文件
Makefile,把每个步骤封装成 make target。比如 make synth 执行综合,make place 执行布局。这样团队里其他人也能快速上手。
3.7 知识结构总览
下面这张图总结了本章的核心内容——设计输入文件的类型、作用以及它们在 OpenROAD 流程中的位置:
3.8 文件准备检查清单
最后,我整理了一份检查清单。每次开始新项目,我都会对着这个清单过一遍:
- RTL 文件:是否所有模块都在?是否有语法错误?(用
verilator --lint-only快速检查) - Liberty 文件:corner 是否齐全?版本是否与 LEF 匹配?
- LEF 文件:技术 LEF 和单元 LEF 是否都有?是否有缺失的 macro?
- DEF 文件(如果有):坐标是否合理?是否有重叠的单元?
- SDC 文件:时钟定义是否完整?是否有未定义的端口?
- 目录结构:是否按规范搭建?脚本路径是否与目录一致?
好了,输入文件准备就绪,目录结构也搭好了。下一步,咱们就可以开始真正的 OpenROAD 流程了——从综合开始,一步步把 RTL 变成 GDSII。