具体介绍使用Synopsys的Design Compiler进行综合的过程,整个过程大致可以分为以下几个部分:
其中预综合过程包含以下内容:
更加具体的流程如下:
逻辑综合的相关一些基础知识:【DC】逻辑综合基础
1. DC启动
1.1 启动方式
dc_shell [-f script]
启动后,使用 DCSH 命令,DCSH 是基于 Synopsys 自身语言的命令行接口。( 也就是说这种启动方式只能使用Synopsys TCL)
dc_shell -t [-f script]
该命令则可以使用标准 TCL 语言。(自动化程度更高)
- Batch Mode
批处理模式,在启动的同时,告诉DC执行哪些脚本文件,基本启动命令如下:
dc_shell -topo -f run.tcl | tee -i run.log
该命令的含义是:使用拓朴模式启动DC,启动的同时执行 run.tcl 脚本文件,并且把命令执行过程中(就是逻辑综合的过程)显示在终端的信息记录到 run.log 中。
拓朴模式:
DC的一个功能,能够在综合阶段精确的预测 post-layout 的时序、面积和功耗,而不是基于线负载模型进行近似计算。
design_analyzer
、design_vision
两个都是启动图形化界面。
design_analyzer
对应的是dc_shell
的命令行方式, 就是说不能在 design_analyzer 里运行普通的 TCL 命令。 另外需要注意的是: design_analyzer
的工作模式不是用于编辑电路图的, 它只能用于显示 HDL 语言描述电路的电路图。
design_vision
是与 TCL 对应的图形方式。
使用design_vision
启动如下:
此时自动打开的图形化界面如下:
这里,先使用 dc_shell
,再使用gui_start
也可以达到相同的效果。
1.2 日志文件
DC启动时,会在启动目录下,生成如下三个日志文件:
所以可以在文件管理的时候,建立一个 work 文件夹,然后再该文件夹下启动 DC 工具。
1.3 启动文件
Synopsys产品很类似,他们都有启动文件.synopsys_dc.setup
,就是 DC 在启动的时候首先读入的文件,用于初始化工具,其中 PT 需要单独的启动文件 .synopsys_pt.setup
。该文件是一个隐藏文件,需要使用ls -a
来查看。
DC在启动的时候, 会自动按顺序在以下三个目录下搜索该文件(如下图所示),对 DC 的工作环境进行初始化:
- $SYNOPSYS/admin/setup 目录下, DC 安装的标准初始化文件
- 当前用户的 $HOME 目录下, 一般用于设置一些用户本人使用的变量以及一些个性化设置。
- DC 启动所在的目录下,一般是和具体设计相关的特定设置。
按照上述三个启动文件的顺序,后面读取的 setup 文件可以覆盖前面文件中的设置。 该文件主要包括库的设置、 工作路径的设置以及一些常用命令别名的设置等等。由于 dcshell 的启动脚本和 TCL 的脚本语法不一致, 所以如果只有一种方式的启动脚本,那么运行另一种方式的时候会报错。 因此, DC 的启动脚本有一种兼容两种方式的格式,如下所示:
第一行的 #
就表示兼容两种格式的命令,不过一般是保持启动方式与脚本语法一致。
文件后面的第一段设置工艺库和链接库, 第二段设置符号库和搜索路径, 第三段设置 DC 命令的别名(alias)。
1.4 启动文件示例
官方实验手册,lab1的中的一个示例。
此外,还可以将输入、输出文件的路径设置为一个变量,指定综合过程中使用到的各种库文件,定义一些命名规则,以及在终端打印输出一些提示信息等。。。
这里面,为了方便管理,会把路径变量的定义放在common_setup.tcl
中,把库文件的变量的定义放在 dc_setup.tcl
中。然后使在.synopsys_dc.setup
文件中使用source
命令来调用这些这两个文件。
除此之外,当然也可以source
一些自定义的内容。
这里,
set
是用于定义一些自定义的变量;
set_app_var
是用于定义DC内部的系统变量,指定DC内部的连接等,主要包括 search_path, synthetic_library, target_library, link_library, symbol_library
以及其他的一些命令开关等。
2. 读入设计文件
DC读入设计文件有两种方式,一是read
指令,二是 analyze
和 elaborate
的组合。
2.1 read
具体示例如下:
read -format verilog[db, vhdl ...] file // DCSH mode
read_db file.db // Tcl mode
read_verilog file.v // Tcl mode
read_vhdl file.vhd // Tcl mode
DC可以读取设计流程中任何一种数据格式,如行为级描述、RTL级描述、门级网表等,但是不同数据格式对应的DC综合的起点不同,也就是说,即使实现相同的功能,也可能会产生不同的结果。
2.2 analyze&elaborate
Noets:
- 该命令仅能用于 VHDL 和 verilog。
read
命令其实是一步完成analyze&elaborate的工作,- 该组合命令和
read
都是读入设计文件,如果只进行综合,更推荐使用组合命令,两者的一些区别如下:
语法格式:
analyze [-library library_name] [-format verilog | vhdl | sverilog] file_list
- library:指定中间结果存放的库,即 Linux 系统下的一个目录,默认为当前目录,也可以用 define_design_lib 命令指定。
- format:HDL 源文件的类型,即 verilog 或 vhdl 或 sverilog 。
- file_list:源代码文件名列表,文件名的指定可以通过TCL的链表命令 {}、“”、[list file_name] 等三种方法之一完成。
elaborate design_name [-library library_name] [-parameters param_list]
- deisgn_name:指需要进行描述的设计。
- library:用于保存设计的结构级描述。
- parameters:指定给设计传递的参数,若省略则参数使用缺省值;参数用“”括起来。
示例:
dc_shell-t>analyze -f verilog [list ctrl_0723.v、datapath_0723.v、ram16s.v、ram16x1s.v、regfile.v、xram16.v、xr16.v、memctrl.v、vga.v、xsoc.v]
2.3 link
当读取完所有要综合的模块之后,需要使用link
命令将读取到 DC 存储区的模块或者实例连接起来。
link
前还需要使用current_design
指定顶层模块为当前设计,DC默认将最后读入的一个模块作为顶层模块。
link
后还需要使用uniquify
进行实例唯一化。
【对于被多次实例化的同一子设计,最终是要映射到物理单元上去的,各个示例的工作环境可能各不相同,因此,需要用 uniquify 命令为每个实例在内存中创建一份副本,以便区分开每个实例。DC可以根据不同的应用环境进行合适的优化】
示例:
dc_shell-t> current_design topModuleName # 设置顶层设计为当前设计
dc_shell-t> link # 链接
dc_shell-t> uniquify # 实例唯一化
3. 施加设计约束
Design Compiler 是一个约束驱动的综合工具,它的结果是和设计者施加的约束条件密切相关的。约束条件主要包括时序约束和面积约束两部分。
【所谓约束,其实就是给工具要给目标,工具会按照这个目标在单元库中挑选合适的标准单元】
DC综合是一个迭代的过程,会对RTL代码进行修改,直到满足时序约束为止,如下图:
前面介绍了 DC 的设计对象,各种约束的施加其实就是对设计对象设置了相应的属性。设计对象的属性按照cell、clock、design、library cell、 net、 pin、 port、 read-only、 reference等进行划分,通常用到较多的 attribute 包括:area、 dont _touch、 load 、rise_drive、fall_drive, max_rise_drive、max_fall_drive、 rise_delay 、fall_delay、 plus_uncertinty、minus_uncertainty, propagated_clock, pin_direction. Port_direction。
查看特定设计对象的特定属性时,可以使命令get_attributes
,比如,查看时钟端口 CIK 的上升沿驱动能力:
dc_ shell-t> get_attribute [get ports CLK] rise_drive
3.283230
再比如查看工艺库smic18ss 中单元 NAND2BIHIDIX的面积:
dc_shell-t> get_attribute [get_lib_cells smicI8_ss/NAND2B1HD1X] area
7.564000
3.1 面积约束
面积是主要的成本因素,电路面积要尽可能小,速度要尽可能快,但速度越快面积就要大。
面积约束指令示例:
dc_shell-t> current_design PRGRM_CNT_TOP
dc_shell-t> set_max_area 100
上面的例子给PRGRM_CNT_TOP
的设计施加了一个最大面积100单位的约束。100 的具体单位是有 Foundary 规定的,定义这个单位有三种可能的标准:
- 两输入与非门的个数(2-input-NAND-gate)
- 晶体管个数(transistors)
- 实际的面积,如平方微米(square mils)
对于时序优先的设计,通常设置其最大面积为0,意指在时序收敛的前提下尽可能的优化面积。命令为:
dc_shell-t>set_max_area 0.0
3.2 时序约束
逻辑综合的过程中,除了面积之外,要达到的目标是约束电路中的所有时序路径,从而实现对工作频率的约束。
设计中的时序路径可以划分为以下四种:
- 输入到寄存器的路径
- 寄存器到寄存器之间的路径
- 寄存器到输出的路径
- 输入到输入的路径
对于图中的路径X,是一个寄存器到寄存器的路径,对于该组合路径而言,其最大裕量是一个时钟周期,因此定义时钟周期就可以实现对该路径的约束。
定义时钟(create_clock)
在电路综合的过程中,所有的时序电路和组合电路的优化都要以时钟为基准来计算路径延迟,因此,一般都要在综合的时候指定时钟,将其作为估计路径延时的基准。
对时钟进行定义的具体内容如下:
时钟定义语法格式:
create_clcok [-name clock_name] [source_objects] [-period period_value] [-waveform edge_list]
# name:为创建的时间指定一个名称,如果省略该项,时钟名称为 source_objects 中第一个对象的名称,如果后续 source_objects 省略,则该项必须指定,这种情况下创建的是虛拟时钟,用于约束组合逻辑的相对延时条件;
# source_objects:用于创建时钟的引脚或端口名称,注意这里是对象;
# -period:创建时钟的周期,单位由工艺库决定,一般为 ns;
# -waveform:指定时钟上升沿和下降沿的时刻,从而决定时钟信号的占空比;如果略去该项,默认时钟占空比为50%,上升沿的时刻为0。
- 定义一个时钟,该时钟域(clock domain)中的所有的同步电路都以该时钟为基准。
- 定义时钟时可以不指定时钟源(source pin or port),此时必须指定时钟名,该时钟(没有指定时钟源的时钟)为虚拟时钟(Virtual Clock), 在实际的电路中没有对应的 clock port 或 clock net。主要是用于作为电路中输入到输出的组合逻辑电路的时钟基准。
set_dont_touch_network
由于时钟信号是驱动大负载的。在综合的时候综合工具会对负载进行估 计,从而在该网络上加上一些有足够驱动能力的 buffer 或者反相器,以使得电路的上升时间和下降时间能够满足要求。而前端工具无法知道连线的走向和长度,在估计时钟网络的负载时不准确,而且 floorplan 的结果将会 影响连线的长度,从而影响连线的负载,因此,前端工具不对大负载的网络进行处理,而把这个工作留到后端。所以在综合的时候需要告诉综合工具不对时钟网络进行处理。
时钟定义示例1:
dc_shell-t> create_clock -period 10 [get_ports Clk]
dc_shell-t> set_dont_touch_network [get_clocks Clk]
create_clock
定义了一个时钟,set_dont_touch_network
给时钟定义了一个 dont_touch 属性,即综合的时候不对Clk信号行优化。
如果不加这句,DC会根据Clk的负载自动对他产生Buffer,而在实际的电路设计中,时钟树(Clock Tree)的综合有自己特别的方法,它需要考虑到实际布线后的物理信息,所以 DC不需要在这里对它进行处理,就算处理了也不会符合要求。
DC综合是一个理想情况,单元的实际位置并没有固定下来,没有办法实现一个实际的时钟网络。DC得到的时钟是很不准确的,在布局布线的时候还要把这个不准确的时钟网络删除,然后重新做一个正确的网络,因此没必要在该阶段进行时钟综合。
时钟定义示例2 :
比如在输入端口 CLK创建一个周期为10ns,名称为clk的时钟,命令为:
dc_shell-t> create_clock -name clk -period 10 [get_ports CLK]
此时,该时钟的占空比为 50%,若要时钟占空比为 40%,命令可改为:
dc_shell-t> create_clock -name clk -period 10 -waveform [0 4] [get_ports CLK]
create_clock
命令创建的时钟是理想时钟,没有延时。可以通过指定latench,uncertainty
等约束条件来模拟真实时钟,相应的命令为:set_clock_latency
,set_ clock_uncertainty
。
clock_latency
set_clock_latency
命令用于定义时钟网络的延迟,包括source_latency
和network_latency
。
source_latency
是从时钟源到时钟定义位置的延迟,network_latency
是时钟定义位置到触发器时钟输入端的延迟。
语法格式:
set_clock_latency [-source] delay object_list
# -source:指定latency为source_latency,若没有该选项,则为network latency.
# delay:时钟延时的值。
# object_list::指定latency所在的时钟、端口、引脚的对象列表。
示例:
dc_shell-t> set_clock_latency -source 3 [get clocks CLK]
dc_shell-t> set_clock_latency 3 [get clocks CLK]
clock_uncertainty
Latency 指定了时钟的延时,后端版图设计的结果可能会引起时钟网络的时序在一定范围内变化(提前或滞后),因此可以使uncertainty 来为 DC 的综合与优化提供一定的时间裕度。uncertainty 分为 plus uncertainty(保持时间检查时用)和 min uncertainty(建立时问检查时用)。设置uncertainty的命令及其格式为:
set_clock_uncertainty [-setup] uncertainty
# -setup:指定仅用于建立时间检查的uncertainty,默认情况下 uncertainty 同时用于建立时间和保持时间检查。
# uncertainty:时钟不确定性的值。
该选项其实就是针对clock skew、clock jitter 进行约束,保证在这时因素出现的时候,时序也会满足要求,相当于是降低了时钟的有效周期。
generated_clock
实际中在一些复杂设计上可能会用到分频或倍频时钟,DC 不能为这些时钟自动创建时钟对象,这就要用到 create_generated_clock
命令。其语法格式为:
create_generated clock [-name clock_name] -source master_pin [-divide_by divide_factor | -multiply_by multiply_factor] source_objects
# -name:指定生成时钟的名称。
# -source:指定主时钟。
# -divide:指定分频系数。
# -multiply:指定倍频系数。
# source_objects:指定生成时钟所在的端口或引脚列表。
比如,当时钟信号 DIVIDE 是主时钟 SYSCLK 的 2 分频,其命令为:
dc_shell-t>create_generated_clock -name DIVIDE -source SYSCLK divide_by 2 [get_pins U4/Q]
propagated_clock
前面介绍的时钟定义需要手工为其设置各种条件,而对于布图后的时钟信号只需指定其为传播时钟,则 DC 会将时钟树上的延时属性自动加载到所指定的设计对象上,其命令为 set_propagated_clock
。
clock transtion
由于时钟的负载很大,时钟的翻转时间往往也很大,这与实际是不符的,因此需要为时钟信号指定一个固定的翻转时间,命令为set_clock_transtion
。
综合前面几条命令,现要设计创建一个时钟,周期 10ns, network_latency为 1ns, source_latency 为 3ns,时钟翻转时间为 0.3ns,建立时间检查的不确定性为0.5ns,完整的脚本命令为:
create_clock -period 10 [get_ports CLK]
set_clock_latency 1 [get_clocks CLK]
set_clock_latency -source 3 [get_clocks CLK]
set_clock_uncertainty -setup 0.5 [get_clocks CLK]
set_clock_transition 0.3 [get_clocks CLK]
set_dont_touch_network [get_clocks CLK]
注意:当内核逻辑加上 PAD 后,时钟应定义在“时钟PAD”的输出上(假定为D,同时为其设置 dont_touch 属性,并将输出线(假定为 pad_clk_o。)设为ideal_network
。具体脚本命令为:
set T 10
create_clock -period $T -waveform [list 0 $T/2] -name clk [get pins PAD INS/CLK PAD/D]
……
set_dont touch network [get_clocks CLK]
set_ideal_network [get_nets PAD_INS/pad_clk_o]
I/O 端口时序
如下图,通过定义时钟,实现了对路径X的约束,但是路径N和路径S的还没有被约束,这个时候就要定义IO相对clock的时序。
输入延迟(input_delay)
对于输入端口,定义输入延迟。
输入延时指的是被综合模块外的寄存器触发的信号在到达被综合模块之前经过的延时,在图中就是外围触发器的clk-q的延时加上M电路延时。
(制定好输入延时之后,就可以确定出路径N的延时范围,只要其延时在这个范围内,信号就能正确传递。)
语法格式:
set_input_delay delay_value [clock clock_name] [-max] [-min] port_pin_list 。
# delay_value:设定的输入延时的大小,单位由工艺库决定,一般为ns。
# -clock:输入延时所参考关联的时钟;
# -max:最大延时(建立时间)
# -min:最小延时(保持时间)
# port_pin_list:当前设计中设定输入延时的输入端口或内部引脚的列表。
示例1:
dc_shell-t> set_input_delay -max 4 -clock Clk [get_ports A]
[get_ports A]
: 将输入延时施加到A端口上max 4
:端口最大延时(最大延时用于建立时间,最小延时用于保持时间)clock Clk
:延时值的基准时钟(即针对哪个时钟的延时)(电路中有多个时钟域,每个时钟域都有对应的输入和输出引脚,所以要指定时钟)
示例2:
假设待综合电路如上图所示,需要施加的时序约束如下:
create_clock -period 20 [get_ports CLK]
set_dont_touch_network [get_clocks CLK]
set_input_delay -max 7.4 -clcok Clk [get_ports A]
因为外部电路模块中给定的延时说明了worst,所以要将输入延时设为max
。
输出延迟(output_delay)
对于输入端口,定义输出延迟
信号在被综合模块的触发器U3里触发,被外围的一个触发器接收。对外围电路而言,它有一个T电路延时和外围触发器的建立时间。当确定了他们的延时之后,被综合模块内部的输出路径延时范围也就确定下来了。
(输出延时,约束的其实是clk2q的延时,加上组合路径延时,这两部分合起来称为输出路径。)
指令语法格式:
set_output_delay delay_value [-clock clock_name] [-max] [-min] port_pin_list
# delay_value: 设定的输出延时的大小,单位由工艺库快定,一般为ns。
# -clock:输出延时所参考关联的时钟。
# -max:指定最长路径的输出延时。
# -min:指定最短路径的输出延时。
# port_pin_list:当前设计中设定输出延时的输出端口或内部引脚的列表。
示例:
dc_shell-t> set_output_delay -max 5.4 -clock Clk [get_ports B]
输入延迟和输出延迟的概念好像是一样的,只是针对的但当前电路而言有了输入和输出之分。两者都是时钟周期减去输入或输出延时得到当前待综合模块的输入或输出路径的时序约束。
组合逻辑约束
对于纯组合逻辑,一般使用set_max_delay
和set_min_dealy
进行约束,也可以使虚拟时钟。
max_delay
set_max_delay 用于在当前设计中指定从任意起点到任意终点的最大延时。
语法格式为:
set_max_delay delay_value [-from from_list] [-through through_list] [-to to_list] [-group_path group_name]
# delay_valve:指定最大延时的值,单位由工艺库决定,一般为 ns.
# -from:延时约束的起点列表;
# -through:延时路径经过的节点列表。
# -to:延时约束的终点列表.
# -group_path:建立一个时序关键路径的组,用于重点优化。
group_path
选项对编译时间有较大影响,而且该组优化的优先级高,可能会导致其他的时序违例。因此该选项(或单独使用 group_ path命令)通常作为综合优化的最后手段使用。
比如设定从UO的CP端经单元U1和U2的Z端到达U3的A端的最大延时为5.0ns,则命令为:
set_max_delay 5.0 -from [get_pins U0/CP] -through [list UI/Z U2/Z] -to [get_pins U3/A]
min_delay
set_min_delay
用于在当前设计中指定路径的最小延时,与set_max_delay
相对,该命令与set_fix_hold
命令配合使用修正电路的保持时间,如果发生min_delay 违规,在不增大 max_delay 的情况下综合工具会自动添加合适的延时单元来修复违规。语法格式为:
set_min_delay delay_value [-from from_list] [-through through_list] [-to to_list]
# delay_value:指定最小延时的值,单位由工艺库决定,一般为ns。
# -from:延时约束的起点列表。
# -through:延时路径经过的节点列表。
# -to:延时约束的终点列表。
比如设定从UO的Cp端经单元 U1和U2 的Z端到达 U3 的A端的最小延时为 3.0ns,则命令为:
set_min_delay 3.0 -from [get_pins U0/CP] -through [list UI/Z U2/Z] -to [get_pins U3/A]
时序例外
由于每条路径都有 timing_constraints,所以才能进行时序分析,但在某些情况下,有些路径的时序分析没有意义,要想忽略这些路径的分析,就要设定时序例外。
除了前面讲的 set_max_delay
和 set_min_delay
指定的时序例外之外,另有两类时序例外:伪路径和多周期路径,分别有set_false_path
和set_multicycle_path
命令来指定。
伪路径
伪路径也称为虚假路径,指时序分析时不需要关心的路径。DC 不能自动识别伪路径,故需要显示指定。set_false_path
命令用于去掉特定路径上时序约束。
对异步逻辑和逻辑上的虚假路径非常有用。语法格式为:
set_false_path [-from from_list] [-through through_list] [-to to_list]
# -from:伪路径的起点列表。
# -through:伪路径经过的节点列表。
# -to:伪路径的终点列表。
比如设定 ff1 的 CP 端经单元 U1 和 U2 的 Z 端到达 ff2 的 D 端的路径为伪路径,则命令为:
dc_ shell-t>set_false_path -from [get_pins ffI/CP] -through [list U1/Z U2/Z] -to [get_pins ff2/D]
如果设计中有多个时钟,那么由一个时钟域 到另一个时钟域之间的路径为伪路径,需要一一标示,可以重复调用 set_false_path 命令,也可以采用如下的脚本命令:
foreach_in_collection clk1 [all_clocks] {
foreach_in_collection clk2 [remove_from_collection [all_clocks] [get_clocks $clk1]] {
set_false_path -from [get_clocks $clk1] -to [get_clocks $clk2]
}
}
多周期路径
多周期路径指两级相邻寄存器之间的组合逻辑延时较大,不能在1个时钟周期完成。因此需要设定为多周期路径,由set_multicycle_path
命令完成。语法格式为:
set_multicycle_path path_multiplier [-setup] [-hold] [-from form_list] [-through through_list] [-to to_list]
# path_multiplier:指定路径的时钟周期数。
# -setup:指定该路径针对建立时间检查;
# -hold:指定该路径针对保持时间检查;
# -from:多周期路径的起点列表;
# -through:多周期路径经过的节点列表;
# -to:多周期路径的终点列表。
示例:
上图为多周期路径示意图,比如在上图所示的设计中,时钟周期为10ns,而其中一级加法器的延时近60ns,那么约束方法为:
dc_shell-t>create_clock -period 10 [get clocks clk]
de shell-t>set_multicycle_path 6 -setup -to [get_pins c_reg[*]/D]
3.3 DRC约束
设计规则约束指为保证最终电路可以正常工作,而由工艺库定义的必须遵守的规则,也称隐含约束。一般用于限制在电容、翻转时间、扇出等因素影响,下一个单元可以和多少个其他单元相连,这些规则是必须遵守的,且比其他规则具有更高的优先级。设计规则约束由 set_max_transition
、set_max_fanout
和set_max_ capacitance
组成。(工艺库本身制定了,但是可以根据需求,人为指定,更加严苛或者宽松。)
最大转换时间
约束design中的信号、端口、net最大transition不能超过这个值,当然是越小越严苛了,net 的 transition time 取决于net的负载(fanout),负载越大,transition time越大。
翻转时间是指引脚或端口的电平翻转所需要的时间。工艺库中一般都会给出该值,如果要为特定的端口、设计或时钟组指定特定的翻转时间,可以采用 set_max_transition
命令,其语法格式为:
dc_shell-t> set_max_transition transition_value object list
# transition_value: 设定的最大翻转时间。
# object_list :设定其翻转时间为 transition_value 的对象列表.。
示例:
dc_shell-t> set_max_transition 1.0 [all_designs]
# 本例中设定所有设计的最大翻转时间为1.0ns
最大扇出负载
对design,net,output port进行操作,设定的不是具体的电容值。扇出负载值是用来表示单元输入引脚相对负载的数目,它并不表示真正的电容负载,而是个无量纲的数字。
扇出负载是指互连线可以驱动的扇出数目,这与容性负载不同。扇出负载没有量纲,在计算时 DC 把一个驱动引脚所连接的所有输入引脚的扇出负载相加,得出该驱动引脚的扇出负载值,并把该值与设定的max_ fan_out
相此较。工艺库中设定了扇出负载的默认值,通过set_max_fanout
可以进行修改。如果两者不同,综合时会以数值较小,即更严格的值为准。
语法格式:
dc_shell-t> set_max_fanout fanout_value object_list
# fanout_value:设定的max_fanout的属性值。
# object_list:设定的 max_fanout 属性为 fanout_value的端口或设计的对象列表。
示例:
dc_shell-t> set_max_fanout 8 [get designs mem_ctrl]
# 设mem_ctrl 的max_fanout为8,则该设计中所有的互连线都被约束。
补充:load vs. fanout_load vs. fanout
- load 指互连线上各种容性负载的总和,单位是电容的单位。
- fanout_load 是指一个互连线所能驱动的输入引脚的容性负载换算成扇出得到的一个相对值,没有量纲;而且1个输入引脚的容性负载换算成扇出得到的 fanout_load不一定为 1。
- fanout则指一个互连线所能驱动的输入引脚的数目。
命令set_max_fanout 中设定的 fanout_value 指的是 fanoutload。
最大负载电容
max_capacitance属性由命令set_max_capacitance
完成。该命令与set_max_transition
是相互独立的。
语法格式:
dc_shell-t> set_max_capacitance capacitance_value object_list
# capacitance_value:设定的 max_capacitance属性值。
# object_list :设定的 max_capacitance 属性为 capacitance_value 的端口或设计对象列表。
示例:
dc_shell-t> set LOAD_OF_A [load_of_smic13_ss/NAND2HD2X/A]
dc_shell-t> set_max_capacitance [expr $LOAD_OF_A*10] [all designs]
本例设定所有设计的max_capacitance
为 smic13_ss/NAND2HDD2X/A 电容的10倍,即0.0838869,该设计中所有的互连线都被约束。该例中的值可在 DC-Tcl 命令行中直接得到。
3.4 Tcl脚本示例
sh date # 显示时间
remove_design -designs # 移除DC中原有的设计
################################
# set library #
################################
set search_path [lsit ********]
set target_library {tt.db}
set link_library {* tt.db}
set symbol_library {tt.sdb}
################################
# void warning Info #
################################
# 屏蔽掉一些无意义的warning信息
suppress_message VER-130
suppress_message VER-129
suppress_message VER-318
suppress_message ELAB-311
suppress_message VER-936
################################
# read design #
################################
read_file -format verilog ~/example1.v
#analyze -format verilog ~/example1.v
#elaborate EXAMPLE1
current_design EXAMPLE1 # 指定顶层模块,否则会默认把最后一个读入的module设为top
uniquify
check_design
################################
# define IO port name #
################################
set clk [get_ports clk] # 设置变量的clk的值是[get_ports clk],在下面的代码中若出现$clk字样,则表示该变量的值,即用[get_ports clk]代替$clk
set rst_n [get_ports rst_n]
set general_inputs [list a b c]
set outputs [get_ports o]
################################
# set constraints #
################################
# 1 set constraints for clock signals
create_clock -n clock $clk -period 20 -waveform {0 10}
set_dont_touch_network [get_clocks clock]
set_drive 0 $clk # 设置时钟端口的驱动为无穷大
set_ideal_network [get_ports clk] # 设置时钟端口为理想网络 这两条设置保证了逻辑综合阶段的时钟是非常理想的(时钟网络不参与综合)
# 2 set constraints for reset signals
set_dont_touch_network $rst_n
set_drive 0 $rst_n
set_ideal_network [get_ports rst_n] # 复位信号同样这样设置,因为复位信号的重要性仅次于时钟信号
# 3 set input delay
set_input_delay -clock clock 8 $general_inputs
# 4 set output delay
set_output_delay -clock clock 8 $outputs
# 5 set design rule constraints
set_max_fanout 4 $general_inputs
set_max_transition 0.5 [get_designs "EXAMPLE1"]
# 6 set area constraint
set_max_area 0 # 在满足时序的条件下,尽可能减小面积
在DC中,可以使用 dcprocheck run.tcl
对读取的脚本进行语法检查。
4. 施加环境约束
除了时序的DRC等约束外,电路还有一些其他因素影响电路的延时,如下:
- 供电电压、外界温度
- 电平转换时间(transition time) : 输入输出的外围电路的驱动能力和负载大小决定的
- 电路内部的互连线延时
所以这一部分的约束如下:
4.1 工作条件
工作条件包括温度、电压和工艺参数。晶圆厂提供的单元库中单元延时是在一个标准条件下得到的,比如温度为25℃,工作电压为1.8V,工艺参数为1.0。
一旦工作条件发生了改变,电路的时序特性也必将受到影响。以上三者对时序的影响如下:
晶圆厂在建库的时候,已经考虑到了参数变化的影响,所以在工艺库中提供了几种工作条件模型:最好情况、典型情况、最差情况。
在逻辑综合的时候,一般只考虑最差情况(延时最大,用于建立时间检查)和最好情况(延时最小,用于保持时间检查)。
由于在逻辑综合阶段以最大化建立时间为目标,所以工作条件通常设为最坏情況。对应命令为:set_operating_conditions
dc_shell-t> set_operating_ conditions WORST
若同时指定最好情况和最坏情况,对修正保持时间违例比较有用,但自前一般不推荐使用。
dc_shell-t> set_operating_ conditions -max WORST -min BEST
4.2 输出容性负载
为了准确估计模块输出的时序,除了需要知道输出延时外,还需要知道所接电路的负载情况。如果输出负载过大,会加大电路的transition time,影响时序特性。DC默认的输出负载为0,即相当于不接负载,这样综合出来的电路时序显然过于乐观,不能反应实际工作情况。
使用set_load
指令来设定输入负载值,负载值可以是一个具体的值,也可以是工艺库中现有单元的load值。语法格式如下:
dc_shell-t> set_load value objects
# value:设定的容性负载值(非负实数);
# objects:当前设计中设定容性负载的对象列表,包括端口、互连线等。
dc_shell-t> set_load load_of lib/cell/pin
示例:
# 1. 固定值
dc_shell-t> set_load 3 [all_outptus] # 设置所有输出端口的容性负载为 3pf
# 2. 单元load值
# 2.1
dc_shell-t> set_load [load_of my_lib/NAND2HD2X/A] [get_potrs OUT1]
# 2.2 采用 smic13_ss库中NAND2HD2X 的引脚A上的电容的10 倍表示所有输出端口的负载
dc_shell-t> set LOAD_OF_A [load_of my_lib/NAND2HD2X/A]
dc_shell-t> set_load [expr $LOAD_OF_A*10] [all outputs]
4.2 输入驱动强度
为了精确计算输入电路的时序,DC需要知道 input port 的 transition 时间。默认情况下DC假定外部信号的transition time 为 0 ,使用set_driving_cell
允许用户可以自行定一个实际的外部驱动cell,可以让DC能够计算一个实际的(non-zero)transition time。
在DC中,驱动强度用电阻值表示,阻值越小表示驱动能力越大,默认情况下DC认为输入端口的驱动电阻阻值为0,即驱动强度无限大。但0值通常用于时钟和复位等扇出很大的端口。对其他端口,为避免这种不切实际的驱动强度,可以采用set_drive
和set_driving_cell
两条命令来设置输入端口的驱动强度。
set_drive
set_drive
命令通过直接指定驱动电阻值对输入端口或双向端口设置fall_drive或rise_drive 属性。语法格式为:
set_drive resistance [-rise] [-fall] port_list
# resistance:非负的驱动电阻值,该值越小表示驱动强度越大;
# -rise/-fall :指定是rise_drive属性还是fall_drive属性,如果都不指定,则rise_drive=fall_drive=resistance。
# port_list:驱动强度设置为resistance 的输入端口或双向端口列表。
示例:
dc_shell-t> set_drive 0 [list clk rst_n] # 时钟和复位端口的驱动强度设为无穷大。
set_drive
命令可以和drive_of
命令结合使用,比如采用 smicl3_ ss
库中单元IVHID1X
的引脚Z的驱动能力表示clk
和rst_n
的驱动能力,其命令为:
dc_shell-t> set_drive [expr [drive_of smic13_ss/IVHD1X/Z] [list clk rst]
若用 smic13_ss库中单元 INVHD1X 的引脚Z的驱动强度的 8倍表示 clk 的驱动强度,则命令为:
dc_shell-t> set_drive [expr [drive_of smic3_ss/INVHDIX/Z]*8] [get_ports clk]
set_driving_cell
set_driving_cell
命令对输入端口或双向端口的驱动电阻进行建模,将端口的驱动强度设定为驱动单元(只有一个输出)或驱动单元特定引脚的驱动强度,同时也把驱动单元的设计规则约束应用于待指定驱动强度的端口。语法格式为:
set_driving_cell [-lib_cell lib_cell_name] [-library lib] [-pin pin_name] [-no_design rule] port_list
# -lib_cell:用于驱动设计输入端口的库单元名称。
# -library:lib_cell 所在库的名称,若当前仅读入唯一的目标库且驱动单元就在该库中,则该选项可省略。
# -pin:用于驱动设计输入端口的单元引脚名称,若单元只有一个输出,该项可略去;若有多个输出,但不指定,则DC采用第一个找到的引脚。
# -no_design_rule:该选项要求 DC 忽咯驱动单元引脚上的设计规则。
# port_list:设置驱动强度的输入端口或双向端口列表。
示例1:
dc_shell-t> set_driving_cell -lib_cell and2a0 \[get_ports IN1]
示例2:
用smic13I0_02_line_ss库中单元 PLBI8N 的驱动强度表示所有输入端口的驱动强度,其命令为:
dc_shell-t> set_driving_cell -library smic13I0_02_line_ss -lib_cell PLBI8N [all_inputs]
set_ideal_network
如果设计中包含PAD模块,那么在指定时钟、复位等高扇出端口的驱动能力时,应使set_ideal_network
命令,且其强度应施加在 PAD 单元的输出线上。比如,时钟端口经PAD单元RAD_INS送入,PAD_INS的输出引脚连接到互连线PAD_CLK_O上,则其命令设置为:
dc_shell-t> set_ideal_network -no_ propagate [get_nets PAD_INS/PAD_CLK_O]
set_drive
命令只对线性延时模型是准确的,而对非线性延时模型不适用,set_driving_cell
命令对各种延迟模型都适用,故对时钟、复位外的其他端口设置驱动强度时建议使用 set_driving_cell 命令。
4.3 互连线延时
在DC综合的过程中,连线延时是通过设置连线负载模型(wire load model)确定的,连线负载模型基于连线的扇出,估计它的电阻电容等寄生参数,它是也是由Foundry提供的。Foundry根据其他用这个工艺流片的芯片的连线延时进行统计,从而得到这个值。
连线负载模型的标准格式如下:
连线延时的计算是根据连线的扇出,通过查找表计算长度,然后计算电阻和电容。若扇出值超出表中的值(假设为7),那么DC就要根据扇出和长度的斜率(Slope)推算出此时的连线长度。
在每一种工作条件下都会有很多种负载模型,各种负载模型对应不同大小的模块的连线,如上图的模型近似认为是160K门大小的模块适用的。模块越小,它的单位长度的电阻及电容值也越小,负载模型对应的参数也越小。
根据综合后的面积大小,自动选择线负载模型。
具体语法格式:
set_wire_load_model -name model_name [-library lib]
# -name:线负载模型的名称
# -library:包含所指定线负载模型的库名称,若当前读入的目标库唯一则该选项可省略。
4.4 线负载工作方式
前以小节的线负载模型考虑的都是同一模块内部的连线,对于连接不同模块之间的连线,其线负载模型需要通过设定连线负载模式(wireload model mode)来指定。
连线负载模式包括围绕(enclosed)、顶层(top)、分段(segmented)三种。设定指令为:set_wire_load_mode
如下图所示,一根连线连接了B2和B1两个模块,这两个模块都位于TOP下的SUB这个子模块中。
- 围绕(enclosed):连接B1和B2的连线的负载模型用围绕它们的模块的负载模型代替,即用SUB的负载模型;
- 顶层(top):用顶层模块的负载模型代替;
- 分段(segmented ):分别根据穿过的三段的模型相加得到。
4.5 约束检查
在定义完环境属性之后,我们可以使用下面的几个命令检查约束是否施加成功。
check_timing
:检查设计是否有路径没有加入约束check_design
:检查设计中是否有悬空管脚或者输出短接的情况write_script
:将施加的约束和属性写出到一个文件中,可以检查这个文件看看是否正确。(该文件很重要,后续的设计的流程都要以该部分内容为时序基准)
5 编译和输出
在完成设计约束、DRC、环境约束之后,就可以进行编译,然后得到逻辑综合的结果和输出文件。
(在编译之前还需要进行一些优化策略的选择,后续再了解这一部分)
5.1 编译指令
compile
命令进行设计或模块的编译,将HDL代码映射到指定的目标库。其选项可用于设计的映射优化,语法格式为:
compile [-map_effort medium|high]
[-area_effort none|low|medium|high]
[-incremental_mapping]
[-ungroup_all]
[-auto_ungroup area|delay]
[-no_design_rule | -only_design_rule | -only_hold_time]
# -map_effort:综合映射的努力程度,有medium和high两个选项,缺省默认为medium项,缺省为值与 map_ctfort 的值相同。
# -area_effort:综合器面积优化的努力程度,有1ow,medium,high三个选项,缺省默认为medium;
# -incremental_mapping:称为增量编译,指示综合器在前一次综合结果的基础上进一步优化,只用在门级,通常用于改善时序和修正 DRC违规。增量编译时,DC 会进行各种映射优化以改善时序。若只对设计修正DRC,可以加上
# -only_desien_rule 选项;
# -ungroup_all:对设计中除了具有dont_touch属性的设计外全去除层次。
# -auto_ungroup:对设计中的层次结构根据需要自动去除层次。
比如,在初次编译时采用 medium 的映射努力,增量编译时采用high的映射努力,相应命令为:
#1st compile
compile -map_effort medium
#2nd compile
current_design xsoc
compile -incremental -map_effort high
编译时,DC提供了compie_ultra
命令,包括许多高级的综合优化算法,可以使关键路径的分析和优化在最短时间内完成。
-
通过
compile
命令把 RTL 映射成门级网表同时优化里面的逻辑。(一般compile执行2到3次)
一般在compile
完成后可以用-inc
在compile
基础上进一步优化
可以用complie_ultra
(超级compile)更进一步优化。但是需要额外 licence -
在使用
compile
时加上-scan
参数则会把所有的触发器替换成带sacn端口的触发器(现在一般用Tessent加扫描链);主要为DFT做准备;一般会增大20%-30%(经验值);
5.2 输出文件内容
以具体脚本为例,介绍输出的具体内容,如下所示:
################################
# complie_design #
################################
compile -map_effort medium
################################
# write *.db and *.v #
################################
write -f db -hier -output ~/EXAMPLE1.db # 保存整个工程
write -f verilog -hier -output ~/EXAMPLE1netlist.v # 逻辑综合后的网表,用于布局布线和后仿真
write_sdf -version 2.1 ~/EXAMPLE1.sdf # sdf反标文件,标注了使用的标准单元的延时值,后仿真也需要文件
################################
# generate reports #
################################
report_area> EXAMPLE!.area_rpt # 面积报告,包括时序电路面积、组合逻辑电路面积、总面积
report_timing> EXAMPLE1.timing_rpt # 时序报告,查看建立时间和保持时间是否满足要求
report_constraint -all_violators> EXAMPLE1.constraint_rpt # 违例报告,逻辑综合过程中没有达到的约束
sh date # 显示时间
时序报告
- 用
check_design
检查设计中是否存在其他隐患问题 - 用
check_timing
检查设计中是否有路径没有被约束,每一条路径都应该约束到,如果真的有哪一条不需要约束也应该设置 成false path。 - 用
report_qor
查看整体综合后的summary结果 - 用
report_area
查看综合后得到的面积 - 用
report_timing
查看具体的setup/hold时序信息。 - 用
write_file -f verilog
写出综合后的网表文件给后端 - 用
write_file -f ddc
保存当前综合的数据data base。下次可以直接load后查看结果。不需要重新综合。 - 用
write_sdf
写出综合后的sdf文件。 - 综合后的 sdf 没有什么太大的意义,因为时钟网络是理想的,并且也不需要修hold
- 最差的时序才是critical path
- set_critical_path对次关键路径优化
时序报告的内容主要包括:表头、数据发射路径、数据捕获路径、时序结果
电路综合完成之后,可以用 report 命令将电路的一些信息 report 出来分 析,阅读报告时主要需要注意几个部分:
- 看看报告中的综合库、线负载模型、工作条件等是不是你所要求的。
- 看看报告的路径是最大路径还是最小路径,即看报告中的 Path type 为 max,该路径是为了满足 library 中 FF 的 setup time 的要求。
- 看路径中是否有很大延迟的单元,或者输入/输出延迟是否很大
- 看关键路径上是否有 slack
- 这样综合完成之后,对于最差工作环境下的库如果没有问题,对于最好的工作情况,可能会出现 Hold time violation. 这时可用
set_fix_hold [all_clocks]
,之后再compile
, 来让 DC 对电路中的 hold time violation 进行 处理。 - 如果在综合之后发现电路中出现 violation,即电路的时序不能满足要求,
当出现的 slack 比较小时,可以使用一些综合的选项来减小这些 slack; 如果 slack 比较大,通过综合的这些选项无法解决,则需要返回重新写 HDL 源代码。 一般在综合时 violation 较小时,可以用compile –inc –map high
来减小路 径的延迟
设计综合(优化)
电路模块设计规划(以利于更好的进行约束),Design Compiler综合优化的过程(三大优化阶段,结构级,逻辑级,门级),时序分析的具体过程等综合过程中的一些详细信息。
(更加具体的优化策略后续再看)
后综合过程
综合完毕该怎么看结果,时序违反该如何解决?这就是后综合过程所要解决的问题。在综合之后,通过分析综合报告,可以得知此次的电路综合结果如何,根据不符合的要求,进行重新约束,甚至重新设计电路。在这个阶段特别值得一提的是综合预估,因为在写综合约束脚本的时候,需要确定约束条件,规格书一般不能够涉及到如此细节的部分,所以需要根据实际电路进行综合预估,这个步骤是在代码编写完之后,与验证同时进行的,目的在于大致估计电路是否符合要求,此时的预综合过程与正式的综合过程是一样的,但要求会宽松许多,时序违反的要求大概为 10%-15%,也就是说电路即使有10%-15%的电路不满足时序也没有关系。