(1)如何快速做一个逻辑综合

一、逻辑综合是什么

1. 逻辑综合的定义

逻辑综合是指将硬件描述语言编写的 RTL 代码转换成门级网表的过程。

常见的硬件描述语言包括:

  • VHDL
  • SystemVerilog
  • Verilog

综合前的 RTL 代码主要描述电路功能;综合后的门级网表则由具体标准单元库中的逻辑门、触发器、多路选择器等单元组成。


2. 为什么要做逻辑综合

RTL 代码本身并没有直接和制造工艺绑定。

经过逻辑综合之后,设计会被映射到具体工艺库中的标准单元上,从而生成后端物理设计可以继续使用的门级网表。

逻辑综合不仅仅是代码转换,还包括对电路进行优化和平衡,使设计尽量满足以下目标:

  • 功能正确
  • 速度满足时序要求
  • 面积尽量小
  • 功耗尽量低

这些目标通常合称为:

1
PPA = Performance + Power + Area

也就是:

  • Performance:性能 / 速度
  • Power:功耗
  • Area:面积

3. 什么时候做逻辑综合

逻辑综合一般发生在前端设计完成到一定程度之后。

当 RTL 功能设计基本完成,并且准备进入后端物理设计阶段时,就需要进行逻辑综合。

因此,在一些公司或流程中,逻辑综合也会被称为“中端设计”。

整体流程可以简单理解为:

1
2
3
4
5
6
7
RTL 设计

逻辑综合

门级网表

后端物理设计

二、Design Compiler 基本使用方法

Design Compiler,简称 DC,是常用的逻辑综合工具。

1. 打开 DC

在终端输入:

1
dc_shell

即可进入 DC 的交互式命令环境。


2. 直接执行 TCL 脚本

如果已经写好了完整的综合脚本,可以使用下面的命令运行:

1
dc_shell -f syn.tcl | tee dc.log

含义说明:

命令部分 作用
dc_shell 启动 DC
-f syn.tcl 执行指定的 TCL 脚本
tee dc.log 将终端输出同时保存到 dc.log 文件中

这样做的好处是:

  • 可以自动执行完整综合流程
  • 可以保存综合日志
  • 方便后续查看报错和 warning

三、DC 查询命令的使用

1. 查询某个命令是否存在

可以使用:

1
help *命令关键字*

例如:

1
help *clock*

该命令可以查询和 clock 相关的 DC 命令。


2. 查看某个命令的详细用法

可以使用:

1
man 命令名

例如:

1
man create_clock

该命令会显示 create_clock 的详细说明、参数和使用方法。


3. 退出帮助页面

如果查看帮助时出现很多内容,并且屏幕下方显示:

1
--More--

此时可以按:

1
q

退出当前帮助页面。


四、TCL 综合脚本示例

下面是一个简单的 DC 逻辑综合 TCL 脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#------------------------------
#set initials

set DESIGN_NAME my_module
set TAG justrun

# 设置使用的 CPU 核数
set_host_options -max_cores 8

# 定义 WORK 库,用于存放中间文件
define_design_lib WORK -path ./WORK

# 设置结果输出目录
set RESULTS_DIR ./results/${TAG}
if {![file exists $RESULTS_DIR]} {
file mkdir $RESULTS_DIR
}

#-----------------------------
#set library path

# 设置库文件搜索路径
set_app_var search_path "/tools/Lib/digit_lib/smic180/std/synopsys"

# 设置目标库
# 一般综合时使用 slow 库,因为如果最慢角都能满足时序,说明设计比较稳妥
set_app_var target_library "slow.db"

# 设置 link_path
# target_library 只是告诉 DC 要用哪个库文件
# link_path 才是让 DC 在链接设计时真正可以找到并使用这些库
set_app_var link_path "* slow.db"

#-----------------------------
#elaborate and analyze

# 读取 RTL 文件
read_file -format sverilog "./sources/${DESIGN_NAME}.sv"

# 设置当前设计
current_design ${DESIGN_NAME}

# 链接设计与库文件
link

# 输出综合前的中间文件
write_file -hierarchy -format ddc -output ${RESULTS_DIR}/${DESIGN_NAME}.precompile.ddc
write_file -hierarchy -format verilog -output ${RESULTS_DIR}/${DESIGN_NAME}.precompile.v

#-----------------------------
#add constraint

# 创建时钟约束,周期为 1.0 ns
create_clock -period 1.0 [get_ports clk]

#-----------------------------
#compile

# 执行综合优化
compile_ultra

#-----------------------------
#write output files

# 输出综合后的网表和 ddc 文件
write_file -format verilog -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.postcompile.v
write_file -format ddc -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.postcompile.ddc

# 修改命名规则
# 综合生成的 Verilog 网表中可能会出现反斜杠等特殊符号
# 后端工具读入时可能会有问题
# change_names 可以把这些不规范命名转换成 Verilog 兼容格式
change_names -rules verilog -hierarchy

# mapped.v 是最终交给后端使用的门级网表
write_file -format verilog -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.mapped.v
write_file -format ddc -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.mapped.ddc

exit

五、脚本关键命令解释

1. 设置设计名称

1
set DESIGN_NAME my_module

表示当前要综合的顶层模块名是 my_module

这个名字通常要和 Verilog / SystemVerilog 代码中的顶层模块名一致。


2. 设置运行标签

1
set TAG justrun

TAG 用来区分不同综合结果的输出目录。

例如:

1
./results/justrun

后续如果做多次实验,可以改成:

1
2
3
set TAG run_1
set TAG timing_opt
set TAG area_opt

这样方便管理不同版本的综合结果。


3. 设置 CPU 核数

1
set_host_options -max_cores 8

表示 DC 最多使用 8 个 CPU 核心进行综合。

核数设置得合理可以提高运行速度。


4. 定义 WORK 库

1
define_design_lib WORK -path ./WORK

WORK 是 DC 用来存放中间分析结果的设计库。

如果不定义,工具可能无法正确保存和管理中间文件。


5. 设置结果目录

1
2
3
4
set RESULTS_DIR ./results/${TAG}
if {![file exists $RESULTS_DIR]} {
file mkdir $RESULTS_DIR
}

这段代码的作用是:

  • 设置综合结果保存路径
  • 如果目录不存在,就自动创建目录

6. 设置搜索路径

1
set_app_var search_path "/tools/Lib/digit_lib/smic180/std/synopsys"

search_path 用来告诉 DC 去哪里查找库文件、RTL 文件或其他相关文件。

设置之后,后续使用库文件时可以直接写文件名,不一定要写完整绝对路径。


7. 设置目标库

1
set_app_var target_library "slow.db"

target_library 是综合时用于映射的目标标准单元库。

简单理解:

1
2
3
RTL 代码中的逻辑

映射到 target_library 中的标准单元

例如:

  • 与门
  • 或门
  • 非门
  • 触发器
  • 多路选择器

都会从 slow.db 这个库中选择对应单元。


8. 设置链接库

1
set_app_var link_path "* slow.db"

link_path 用来告诉 DC 在链接设计时可以去哪些库中寻找模块或标准单元。

其中:

1
*

表示当前已经读入 DC 的设计。

slow.db 表示标准单元库。

所以这句话可以理解为:

1
链接时,既要查找当前设计,也要查找 slow.db 标准单元库。

9. 读取 RTL 文件

1
read_file -format sverilog "./sources/${DESIGN_NAME}.sv"

表示以 SystemVerilog 格式读取 RTL 文件。

如果:

1
set DESIGN_NAME my_module

那么实际读取的文件就是:

1
./sources/my_module.sv

10. 设置当前设计

1
current_design ${DESIGN_NAME}

告诉 DC 当前正在处理的顶层设计是哪一个。


11. 链接设计

1
link

link 的作用是将 RTL 设计和库文件连接起来。

如果设计中例化了子模块,或者需要使用标准单元库,link 就会检查这些模块和单元能不能找到。

如果找不到,就可能出现 unresolved reference 之类的错误。


12. 输出综合前文件

1
2
write_file -hierarchy -format ddc -output ${RESULTS_DIR}/${DESIGN_NAME}.precompile.ddc
write_file -hierarchy -format verilog -output ${RESULTS_DIR}/${DESIGN_NAME}.precompile.v

这两条命令用于保存综合前的设计状态。

其中:

文件 作用
.ddc DC 自己使用的二进制设计文件
.v Verilog 格式文件

13. 创建时钟约束

1
create_clock -period 1.0 [get_ports clk]

表示给端口 clk 创建一个周期为 1.0 ns 的时钟。

周期 1.0 ns 对应频率为:

1
1 / 1 ns = 1 GHz

这个约束会告诉 DC:

1
电路中的寄存器到寄存器路径必须尽量在 1.0 ns 内完成。

14. 执行综合

1
compile_ultra

compile_ultra 是 DC 中常用的高强度综合优化命令。

它会根据约束对设计进行优化,主要目标包括:

  • 修复时序
  • 减小面积
  • 优化逻辑结构
  • 进行技术映射

15. 输出综合后文件

1
2
write_file -format verilog -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.postcompile.v
write_file -format ddc -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.postcompile.ddc

这两条命令用于输出综合后的结果。

此时的 Verilog 文件已经不再是纯 RTL,而是综合后的门级结构。


16. 修改命名规则

1
change_names -rules verilog -hierarchy

综合后的网表中可能会出现一些特殊命名,例如带有反斜杠的名字。

这些名字虽然 DC 可以识别,但后端工具读入时可能会不方便。

因此需要用 change_names 将名称转换成更规范的 Verilog 命名形式。


17. 输出最终门级网表

1
2
write_file -format verilog -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.mapped.v
write_file -format ddc -hierarchy -output ${RESULTS_DIR}/${DESIGN_NAME}.mapped.ddc

其中:

1
mapped.v

通常就是最终交给后端 APR 工具使用的门级网表。


六、综合后的数据怎么看

综合后,DC 会输出一些统计信息。

例如寄存器统计:

1
2
3
4
5
===============================================================================
| Register Name | Type | Width | Bus | MB | AR | AS | SR | SS | ST |
===============================================================================
| register_file_reg | Flip-flop | 128 | Y | N | Y | N | N | N | N |
===============================================================================

可以看出:

字段 含义
Register Name 寄存器名称
Type 类型,例如 Flip-flop
Width 位宽
Bus 是否是总线
AR 是否有异步复位
AS 是否有异步置位
SR 是否有同步复位
SS 是否有同步置位

例如:

1
register_file_reg

是一个宽度为 128 bit 的触发器寄存器。


七、MUX 统计信息

综合后还可能看到 MUX 相关统计,例如:

1
2
3
4
5
6
Statistics for MUX_OPs
======================================================
| block name/line | Inputs | Outputs | # sel inputs |
======================================================
| my_module/32 | 4 | 32 | 2 |
======================================================

这表示:

my_module 的第 32 行,DC 推断出了一个多路选择器。

具体含义如下:

字段 含义
block name/line 模块名和代码行号
Inputs MUX 输入数量
Outputs MUX 输出位宽
# sel inputs 选择信号位数

这一行:

1
|   my_module/32   |   4    |   32    |      2       |

可以理解为:

1
在 my_module 第 32 行,推断出了一个 4 选 1、输出位宽为 32 bit 的多路选择器。

因为 4 选 1 MUX 需要 2 bit 选择信号:

1
2
2 bit 选择信号可以表示 4 种情况:
00、01、10、11

八、一个最小 DC 综合流程总结

一个最基础的逻辑综合流程可以总结为:

1
2
3
4
5
6
7
8
9
10
1. 设置设计名称
2. 设置库路径
3. 设置 target_library
4. 设置 link_path
5. 读取 RTL 文件
6. 设置 current_design
7. link 设计
8. 添加时钟约束
9. 执行 compile_ultra
10. 输出 mapped.v

对应的简化 TCL 流程为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
set DESIGN_NAME my_module
set_app_var search_path "/tools/Lib/digit_lib/smic180/std/synopsys"
set_app_var target_library "slow.db"
set_app_var link_path "* slow.db"

read_file -format sverilog "./sources/${DESIGN_NAME}.sv"
current_design ${DESIGN_NAME}
link

create_clock -period 1.0 [get_ports clk]

compile_ultra

change_names -rules verilog -hierarchy
write_file -format verilog -hierarchy -output ./results/${DESIGN_NAME}.mapped.v

九、学习重点

这篇内容适合刚开始学习 DC 逻辑综合时入门。

重点需要掌握:

  1. 逻辑综合的作用
  2. RTL 和门级网表的区别
  3. target_librarylink_path 的区别
  4. 如何读取 RTL 文件
  5. 如何创建时钟约束
  6. 如何运行 compile_ultra
  7. 为什么要输出 mapped.v
  8. 如何简单查看综合报告中的寄存器和 MUX 信息

十、常见理解误区

误区 1:RTL 代码可以直接给后端使用

不准确。

后端 APR 一般需要的是门级网表,而不是 RTL 代码。

RTL 代码需要先经过逻辑综合,映射成标准单元之后,才能继续进入后端流程。


误区 2:设置了 target_library 就够了

不够。

target_library 只是告诉 DC 综合映射时使用哪个库。

但链接设计时,还需要通过 link_path 告诉 DC 去哪里找这些库和当前设计。


误区 3:postcompile.v 就一定是最终网表

不一定。

通常还需要执行:

1
change_names -rules verilog -hierarchy

然后再输出:

1
mapped.v

mapped.v 才更适合作为后端工具输入。


误区 4:只要能综合成功就说明设计没问题

不一定。

综合成功只说明工具生成了门级网表。

后面还需要继续检查:

  • 时序是否满足
  • 面积是否合理
  • 功耗是否可接受
  • 是否有 warning
  • 是否有 unresolved reference
  • 是否需要做形式验证
  • 是否可以被后端工具正确读入

十一、建议补充的报告命令

实际做综合时,建议在脚本中加入一些报告输出命令,方便查看结果。

1
2
3
4
5
6
report_timing      > ${RESULTS_DIR}/timing.rpt
report_area > ${RESULTS_DIR}/area.rpt
report_power > ${RESULTS_DIR}/power.rpt
report_qor > ${RESULTS_DIR}/qor.rpt
report_constraint > ${RESULTS_DIR}/constraint.rpt
check_design > ${RESULTS_DIR}/check_design.rpt

这些报告的作用如下:

报告 作用
report_timing 查看关键路径和时序 slack
report_area 查看面积
report_power 查看功耗
report_qor 查看综合整体质量
report_constraint 查看约束是否满足
check_design 检查设计是否存在结构或连接问题

十二、最终复习版总结

逻辑综合的核心目的可以概括为一句话:

把 RTL 代码转换成满足时序、面积、功耗要求的门级网表。

一个完整的 DC 入门综合流程大致是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
准备 RTL

设置库路径

设置 target_library / link_path

读取 RTL

current_design

link

添加时钟和 IO 约束

compile_ultra

输出 mapped.v / mapped.ddc

查看 timing、area、power、qor 等报告

最需要重点理解的是:

  • target_library:综合映射时用哪个标准单元库
  • link_path:链接设计时去哪里找当前设计和库单元
  • create_clock:告诉工具时钟周期要求
  • compile_ultra:执行综合优化
  • mapped.v:最终交给后端使用的门级网表
ESC 关闭 | 导航 | Enter 打开
输入关键词开始搜索