Download as pdf or txt
Download as pdf or txt
You are on page 1of 172

Tcl 与 Design Compiler

一、前言
已经学习 DC 的使用有一段时间了,在学习期间,参考了一些书,写了一些总结。

我也不把总结藏着掖着了,记录在博客园里面,一方面是记录自己的学习记录,

另一方面是分享给大家,希望大家能够得到帮助。参考的书籍有很多,大概如下:

 虞希清老师的《专用集成电路设计实用教程》

 西电出版社的《数字 IC 系统设计》

 好像还有《SoC 设计方法与实现》《数字集成电路设计与技术》

 当然,还有 synopsys 公司的 Design Compiler workshop

基本上是参考上面几本书学习 DC 的。在《Tcl 与 Design Compiler》这些学习

记录中,我将记录关于 tcl 和 Design Compiler 的使用知识,重点在后者。


二、DC 综合与 Tcl 语法结构概述
1、逻辑综合的概述

synthesis = translation + logic optimization + gate mapping

DC 工作流程主要分为这三步:

Translation : 翻译。主要把描述 RTL 级的 HDL 语言,在约束下转换成

DC 内部的统一用门级描述的电路(Generic Boolean Gates)(DC 自己的

库表现),以 GTECH 或者没有映射的 ddc 形式展现。

Logic optimization :逻辑优化,就是把统一用门级描述的电路进行优化,

就是把路径调整一下,门给改一下等等。

Gate mapping :门级映射,把优化了的统一门级描述,DC 用别的厂商的

工艺库把电路给映射出来,得到一个.ddc 文件。这个.ddc 文件可以包含许

多丰富的信息,比如映射的门电路信息与网表、.v 格式的网表、延时信息(sdf)、

工作约束(sdc)等信息。(.ddc 不能用文本编辑器打开)。.ddc 这个包含的

网表文件是实际意义的网表文件,而.v 这个形式的网表问价是用来做后仿真

的文件。

延时信息的得出:线负载模型、拓扑结构模型(现在)。

2、DC 的启动方式

启动 DC 的方式有三种:

 GUI:DC 的图形化界面格式。用图形化界面跑一下 DC。(大规模设计不可

能用这种方式)。

启动方式是:$design_vision
我们可以通过 man design_vision 或者 design_vision -help 来查看 DC

的启动选项:

例如,可以使用$ design_vision -topographical_mode 启动 design

compiler,意思是:-topographical 是启动 DC 的拓扑模式,-topo 是简写。

关于什么是拓扑模式,为什么要启动拓扑模式,后面会有相关的叙述。

此外,还有下面的启动方式:

 dc_shell: DC 以命令行的格式启动:$dc_shell

我们可以通过 man dc_shell 或者 dc_shell -help 来查看 DC 的启动选项,

不过我们可以发现,这些选项是一致的,是告诉我们如何启动 DC,启动 DC

的时候可以加载哪些选项。

 Batch mode: 批处理模式,也就是,前面那两种方式只是把 DC 启动起来,

还没有真正地工作(即编译工程),前面两个方式需要通过 source 命令把脚

本写进去以后,DC 读取才真正工作。而这种批处理模式是,在启动的同时,

告诉 DC 执行哪些脚本,例如:

$dc_shell -topo -f run.tcl | tee -i run.log

意思是:使用拓扑模式启动 DC,启动的同时执行 run.tcl 脚本文件,并且把


启动过程中显示在终端的信息记录到 run.log 中。| tee -i 就是写进信息的

管道命令,讲 dc_shell -topo -f run.tcl 执行后显示的信息(输出结果),

流入到 run.log 文件中。这样子是为了在 DC 启动失败的时候,通过查看启

动信息,进而排除错误。

本教程这三种方式在后面都有流程介绍,本教程以命令行和批处理为主,其

中 dc_shell 主要用来介绍 DC 的相关命令,此外命令行和批处理模式是现在

设计的主要操作方式。

3、DC-Tcl 语言的基本结构

下面介绍常见的 tcl 语言语法,这些语法在进行编写 tcl 脚本的时候比较常见。

Tcl 的语法比较简单,依葫芦画瓢就可以知道写的是什么了。

我们在 dc_shell 的环境下,


介绍 tcl 的一些简单语法(即启动 DC,在 dc_shell

里面运行 tcl 文件):

通过编写 example.tcl 中的内容,来记录一下 tcl 的语法:

 设置变量

example.tcl 的内容为:

意思是:设置 name 这个变量,这个变量的值为 ICer;


设置 num 这个变量,这个值为 0;

echo 是打印的意思,$表示引用,分别是打印 name 中的值,打印 num 中

的值.

我们可以检查一下是否有语法错误,使用 dcprocheck 命令:

运行一下,打印出了 name 和 num 这两个变量的值:

 if-else 语句:

与 C 语言、Verilog 语言不一样,这里的条件是用大括号{}来进行包括的,
然后要执行的内容也是通过{}来表示的。特别注意,{}的使用都有空格间隔开

检查没有错误,进行执行:

 switch 结构

puts 也是显示/打印的命令,执行后的结果如下:

还可以添加默认的选项:

执行后,显示:

 while 循环
执行的结果如下:

 for 循环

运行的结果如下所示:
for 循环的格式跟 C 语言一样,也有三个选项,初始条件,停止循环条件,

变量递增选项。

Continue 跟 C 语言一样,不执行本次循环。

 数组(列表)与数组(列表)的遍历

首先,39 行那里创建了一个名字为 names 的数组(列表),数组(列表)

的内容有 5 项。

Foreach 则是逐个遍历,查看数组的内容。首先把 names 这个列表的内容

一次放进 num_list 这个变量里面,然后一次查看

附注:foreach_incollection 对物集(collection)遍历。关于物集这个概

念在后面会有说到,这里先提及一下,有一个初步印象。

执行结果如下:
 子程序的定义和调用:

调用要在 dc_shell 中调用:

 最后是文件的处理:文件的读写

执行的结果如下所示:

Data.txt 里面就有 hello tcl 读:


注意 gets 的用法,执行后的结果如下:

Tcl 的基本语法结构差不多就是这样了,Tcl 的使用需要结合 Tcl 自带的一些

命令,比如说自己的函数、错误处理、正则表达式等,此外还要结合 DC 的

命令,总之就是具体问题具体分析。
三、DC 综合流程
1、基本流程概述

首先给三个图,一个图是高层次设计的流程图:

下面是我对这张图的理解:

1. 设计之前,准备好库、HDL 代码的思想、约束生成;然后根据设计思想

用 RTL 源码详细地、完整地为设计建立模型、定义设计中寄存器结构和

数目、定义设计中的组合电路功能、定义设计中寄存器时钟等等的设计

规格和实现。

2. 完成 RTL 源码设计之后,应让设计开发与功能仿真并行进行:

 在设计开发阶段,我们使用 DC 来实现特定的设计目标(设计规则和
优化约束),以及执行默认选项的初步综合。

 如果设计开发结果未能在 10%的偏差范围内满足时序目标,则需要

修正 HDL 代码,然后重复设计开发和功能验证的过程。

 在功能仿真中,通过特定的工具来确定设计是否能按如所需的功能工

作。

 如果设计未能满足功能要求, 我们必须修改设计代码以及重复设计

开发和功能仿真。继续设计开发和功能仿真直至设计功能正确及满

足小于 10%偏差的时序目标。

3. 使用 DC 完成设计的综合并满足设计目标.这个过程包括三个步骤,即综

合=转化+逻辑优化+映射,首先将 RTL 源代码转化为通用的布尔等式,

然后设计的约束对电路进行逻辑综合和优化,使电路能满足设计的目标

或者约束,最后使用目标工艺库的逻辑单元映射成门级网表,在将设计

综合成门级网表之后,要验证此时的设计是否满足设计目标.如果不能满

足设计目标,此时需要产生及分析报告确定问题及解决问题

4. 当设计满足功能、时序以及其他的设计目标的时候,需要执行物理层设计

最后分析物理层设计的性能,也就是使用 DC 的拓扑模式,加入

floorplan 的物理信息后进行综合分析设计的性能。如果结果未能满足

设计目标,应返回第三步.如果满足设计目标,则本部分设计周期完成.

一个图是 DC 在设计流程中的位置:
这个图将上面的流程图细化,着重与 DC 的部分,描述了使用 DC 进行逻辑综合

时要做的事,同时,也是对前面的流程图解说的图形概述。在综合的时候,首先

DC 的 HDL compiler 把 HDL 代码转化成 DC 自带的 GTECH 格式,然后 DC 的

library compiler 根据标准设计约束(SDC)文件、IP-DW 库、工艺库、图形

库、(使用拓扑模式时,还要加入 ICC 生成的 DEF 模式,加载物理布局信息)

进行时序优化、数据通路优化、功耗优化(DC 的 power compiler 进行)、测

试的综合优化(DC 的 DFT compiler),最后得到优化后的网表。

最后一个图是,使用 DC 进行基本的逻辑综合的流程图与相应的命令:
这个图给出了使用 DC 进行逻辑综合时的基本步骤,我们根据这个图运行 DC,

下面是这个图的具体解说:

1 准备设计文件,DC 的设计输入文件一般为 HDL 文件。

2 指定库文件,需要指定的库文件包括:

链接库(link library) 、目标库(target library) 、符号库(symbol library)、

综合库(synthetic library)下面是库的解释,具体的解释在后面有说,这里先

进行简单地概述一下:Link library & target library

Link library 和 target library 统称为 technology library(即工艺库,习

惯称之为综合库),technology library 由半导体制造商提供,包含相关 cell


的信息及设计约束标准,其中:

Target library

在门级优化及映射的时候提供生成网表的 cell,即 DC 用于创建实际电路的库。

Link library

提供设计网表中的 cell,可以跟 target_library 使用同一个库,但是 DC 不用

link library 中的 cell 来综合设计。

当 DC 读入设计时,它自动读入由 link library 变量指定的库。当连接设计时,

DC 先搜寻其内存中已经有的库,然后在搜寻由 link library 指定的库。

注:当读入的文件是门级网表时,需要把 link library 指向生成该门级网表的库

文件,否则 DC 因不知道网表中门单元电路的功能而报错。 关于工艺库里面的

具体内容,后面会专门进行说明。

Symbol library

Symbol library 提供 Design Vision GUI 中设计实现的图形符号,如果你使用

脚本模式而不使用 GUI,此库可不指定 Symbol library

Synthetic library

即为 Designware library ,名字上翻译是综合库,但却常称之为 IP 库,而不是

直译。特殊的 Designware library 是需要授权的(比如使用多级流水线的乘法

器),默认的标准 Designware 由 DC 软件商提供,无需指定。

Create_mw_lib

主要使用 DC 的物理综合(Physical compiler)的时候,需要生成物理库

3 读入设计 - Read design

设计的读入过程是将设计文件载入内存,并将其转换为 DC 的中间格式,
即 GTECH 格式,GTECH 格式由“soft macros” 如 adders,

comparators 等组成,这些组件来自 synopsys 的 synthetic lib,每

种组件具有多种结构。

读入设计有两种实现方法实现方法:read 和 analyze & elaborate

(实际上 read 是 analyze 与 elaborate 的打包操作 ),下面介绍

二者在使用中的区别:

从中可以看到,analyze & elaborate 可以自由指定设计库,并生成

GTECH 中间文件前生成.syn 文件存储于 work 目录下,便于下次

elaborate 节省时间,我们一般选择 analyze & elaborate 的方法读入

设计。

4 定义设计环境 - Define design environment

定义对象包括工艺参数(温度、电压等),I/O 端口属性(负载、驱动、

扇出),统计 wire-load 模型,设计环境将影响设计综合及优化结果。

5 设置设计约束 - Set design constrains

设计约束包括设计规则约束和优化约束,设计规则约束(design rule

constraint)由工艺库决定,在设计编译过程中必须满足,用于使电路

能按功能要求正常工作。设计优化约束定义了 DC 要达到的时序和面积

优化目标,该约束由用户指定,DC 在不违反设计规则约束的前提下,
遵循此约束综合设计。

6 选择编译策略 - Select compile strategy:

对于层次化设计,DC 中有两种编译策略供选择,分别为 Top-down 和

Bottom-up。在 Top-down 策略中,顶层设计和子设计在一起编译,所

有的环境和约束设置针对顶层设计,虽然此种策略自动考虑到相关的内

部设计,但是此种策略不适合与大型设计,因为 Top-down 编译策略中,

所以设计必须同时驻内存,硬件资源耗费大。在 Bottom-up 策略中,

子设计单独约束,当子设计成功编译后,被设置为 dont_touch 属性,

防止在之后的编译过程中被修改,所有同层子设计编译完成后,再编译

之上的父设计,直至顶层设计编译完成。Bottom-up 策略允许大规模设

计,因为该策略不需要所有设计同时驻入内存。

7 编译 - Synthesis and optimize the design

用 Compile 命令执行综合与优化过程,还可以利用一些选项指导编译和

优化过程。

8 分析及解决设计中存在的问题 -

DC 可以产生一些报告以反应设计的综合和优化结果,如:时序、面积、

约束等报告,这些报告有助于分析和解决设计中存在的问题以改善综合

结果,我们还可以利用 check_design 命令检验综合的设计的一致性。

9 存储设计数据 - Save the design database

DC 不会自动存储综合后的设计结果,因而需要在离开 DC 时手动存储

设计数据。比如存储网表、延时信息等数据文件。

2、实战
在这里,我们将实战一下,做一下实验,让大家体验一下流程:

 首先准备好文件:

这个.synopsys_dc.setup 文件(包含了 common_setup、dc_setup)、

TOP.con 文件是需要我们书写的,这里已经写好了,我们来看一下就好了。

.synopsys_dc.setup 的内容:

然后我们移步到 common_setup.tcl 和 dc_setup.tcl 的内容:

common_setup.tcl 文件定义了库的名字和名称,上面是逻辑库,下面物理

库:

5 行:定义库的搜索路径,当找不到库时,从这个路径中寻找

8 行:定义 target library 使用的库(注意,只是定义一个变量)

10 行:定义图形库变量
15 行:定义顶层设计库的变量名称

17 行:定义 milkyway(参考)库(的位置)

19 行:定义工艺库(的位置)

21 行:定义寄生参数库(的位置)

23 行:定义工艺库和寄生参数库的映射关系库(的位置)

dc_setup.tcl 的内容:

dc_setup.tcl 文件就是指定库了,而不是单单地定义了,set_app_var 是定

义 DC 内部变量,4~7 这是指定搜索路径个各种库的路径和名称

下面的物理库设置中:

13 行:指定 milkyway(参考)库的名称

14 行:指定当前设计的库的名称

16 行:创建 milkyway 库,格式如图上面,需要工艺库、参考、当前设计库

19 行:打开当前的设计库

20 行:加载寄生参数(库)
 启动 DC

这里是流程演示,因此我们使用图形化的方式启动:

design_vision -topo

 读入设计前的检查

-->检查库是否正确设置:

-->检查逻辑库和物理库的一致性:

Check_library

检查可能不会通过,结果不影响综合的话,可以忽略

-->检查寄生参数文件和工艺库文件的一致性:(物理综合的时候需要检查)

check_tlu_plus_files

通过的话会有三个 passed

 读入设计和查看设计

-->读入设计:

read_file -format verilog ./rtl/TOP.v

用法如下所示:
-->设置当前设计

要综合哪个模块,就把哪个模块设置为当前设计;

查看当前设计:current_design

设置当前设计:current_design TOP

-->link 设计

Link 设计,查看当前要综合的设计是否缺少子模块:

link

返回值是 1,说明子模块完整

-->以 ddc 的格式保存未映射的设计(注意需要先创建 unmapped 文件夹):

Write -hierarchy -f ddc -out unmapped/TOP.ddc

我们可以看看 write 的用法:


-->查看内存中的设计和库:

带*的设计为当前设计,要综合哪个模块就 current_design 哪个设计

然后 list_libs 是查看库和库的路径,这个命令也可以检查是否读入了对应的

 约束设计(也就是上面流程图中的各种 set)

我们通过执行约束文件来约束设计:

source TOP.con
其他选项我们在后面会叙述,我们这里只说一下 5 行的 reset_design,这个就

是剔除之前所有的约束,防止影响下面的约束。

 进行综合

Compile_ultra(这是在拓扑模式下进行综合的命令)

 综合后的检查

report_constraint -all (查看是否违规)

report_timing (查看时序报告)

report_are (查看面积情况)

 保存综合后的设计

write -hierarchy -format ddc -output ./mapped/TOP.ddc

总结,大概的流程为:

准备好文件—>启动 DC—>读入设计前的检查—>读入设计和查看设计—>

约束设计—>综合—>综合后检查(与优化)—>保存优化后的设计
四、DC 启动环境的设置
主要内容有:

 启动环境的概述

 路径变量的定义与解释

 库的指定与解释

1、启动环境配置简述

我们按照前面的基本流程使用 DC 进行设置。在启动 DC 之前,首先要配置

DC 的启动环境,也就是那些库的设定。配置 DC 的启动环境主要

是.synopsys_dc.setup。.synopsys_dc.setup 这个文件就是 DC 的配置文件,

它配置了 DC 启动过程中要执行哪些命令、干哪些事。其中,search_path 、

target_library...等等(后面会讲,放在这里给大家留个印象),这些是 DC

内部的变量名称,用来告诉 DC 做那些事的方法。

一般.synopsys_dc.setup 文件有三个:

1 一个在 synopsys 的安装目录下,这个文件最好不要动;

2 一个在用户目录下,这个文件没事也不要动他;

3 还有一个当前工作目录下,也就是启动 DC 的目录下(没有就需要

自己创建),这个是要我们自己写的,这个想怎么动就怎么动。我们配置

DC 的启动环境,就是在启动的目录创建.synopsys_dc.setup 并且修改它。

此外,这个文件名字不能随意更改,按照默认的名字来,DC 在启动的过程

中会自动读取各个名字的文件。(如果改变了它的名字,就需要通过 source

命令 source 一下)。

2、.synopsys_dc.setup 配置文件的书写
如下图所示,一般设计的的目录文件夹管理:

主要关注两个文件夹,一个是 rtl: 放置.v 设计文件,一个是 syn 文件夹:综

合时的信息。进入综合文件夹:

有 5 个文件夹,mapped 是存放综合完成之后的文件的目录,这些文件是经

过综合库映射的。Unmaped 是存放综合时没有经过工艺库映射所得到的文

件目录。Report 是存放报告(比如时序报告、面积报告、DC 启动报告等)

的目录。Script 是存放约束脚本的目录。Work 就是启动 DC 的目录了,在

这个目录里面,我们要进行创建.synopsys_dc.setup 文件并且编写这个文件:

文件的内容主要是指定这个四个库(的名称和路径):

当然这个不是这个文件的全部内容,这个文件的全部内容如下(后面有这个

文件内容的讲解):
3、.synopsys_dc.setup 的讲解

简单的.synopsys_dc.setup 文件如上图所示了,总共 79 行。Echo 那些行

都是在终端打印相应的信息而已,提供我们判断 DC 是否按照我们设定的环

境进行启动以及启动到哪一步。“#”开头的是注释部分。31 行(包括 31

行)之前的 set 语句是进行定义变量,也就是把路径用变量来代替,在进行

设置库的时候,就可以使用这些变量,而不是用长长的路径了,同时也让别

人了解你这个路径是什么意思。依次定义:

①设计目录(SYN_ROOT_PATH)的路径:也就是你的总的设计路径,在这

个路径下面进行仿真、综合、形式验证等等的操作。

②RTL 文件的路径(RTL_PATH):放置 RTL 文件的路径。

③配置文件的路径(CONFIG_PATH):放置 DC 的一些配置文件,这里的

设计没有任何的配置文件,因此没有进行设置。

④脚本路径(SCRIPT_PATH):放置约束脚本的路径。

⑤未映射文件的路径(UNMAPPED_PATH):放置 DC 没有用工艺库进行

映射时得到的文件,也就是 GTECH 格式的文件(什么是 GTECH 文件,最


前面第一点的时候就说了的哈),或者以 ddc 格式保存的未映射的文件。

⑥映射后的文件的路径(MAPPED_PATH):放置 DC 最终优化后得到的结

果,可能是以 ddc 的形式保存。

⑦放置报告的路径(REPORT_PATH):放置 DC 运行过程中的一些报告,比

如启动报告,使用约束脚本运行中的报告,时序报告、面积报告等,通过查

阅这些报告,分析 DC 是否按照我们预料中的情况进行启动、运行,分析设

计是否满足要求等。

⑧DC 运行时的路径(WORK_PATH):启动 DC 时的路径,也就是 DC 工

作时的路径。

⑨DC 的路径(DC_PATH):也就是安装 DC 的路径。

⑩define_design_lib work -path $WORK_PATH:这个就不是定义变量

了,这个是 DC 内部命令格式,指定设计和库的工作路径。

⑩①符号库(Symbol library )的路径(SYMBOL_PATH):指定你符号

库的路径,至于什么是符号库,前面也有解说了。关联一些图形化库,使用

GUI 界面的时候,就可以看到综合得到的门级网表的图形化界面。否则看不

到。

由于我用的是台积电 90 纳米的库,图形化符号库及其路径如下图所示:

第一个就是库的路径看,后面那个是库的名称;由于我们需要定义的是库的

路径,因此自然是写路径,而不是写库的名字。

附注:.sdb 是 DC 使用的库,是给 DC 看的,我们查阅得到的是乱码;而.slib


是给我们看的,我们可以用 vim 打开。

⑩②工艺库(technology library )的路径:也就是链接库(link library)、

目标库(target library) 的路径,这两个库合称为工艺库。至于工艺库里

具体信息,后面会进行讲解。我的库的路径和名称如下:

这里的.db 格式是用来给 DC 用的,也就是说,DC 读取工艺库文件,就是

读取.db 这种格式的文件;.lib 是给工程师看的。此外还有什么 fast、typical、

slow 这些名称,这些是库的工作模式,一般情况下,我们都选择 slow,在

最慢的情况下 DC 进行综合,得到的时序和面积都 OK,那么在其他情况下

肯定都 OK 了的。

.pdb/.plib 是物理库(以前有物理综合的概念,即 physical synthesis,用

的是 physical compiler 吧。现在这个概念用在了 ICC 中了)。

从上面中我们会发现,定义(路径)变量用的都是 set(跟 tcl 语法一样),因

此这里就要强调一下:
set : 自己自定义的一些变量,方便定义系统变量的时候,不用那么麻烦;主

要是定义。

set_app_var:定义 DC 内部的系统变量、指定 DC 内部的连接等。主要指

定有:search_path、synthetic_library、target_library、link_library、

symbol_library 以及其他的一些命令开关等。

当定义完这些“自定义”的变量之后,我们就要做一些正经事了,就是指定

DC 要用到的库了。

⑩③指定搜索路径(Search_path):当读入了一些工艺库时,忘了设置它

的路径,那么 DC 就会根据这个变量设置的路径去寻找库。DC 可以根据这

些路径进行寻找相关的库文件;或者,当需要多个.v 文件时,通过这个变量

告诉 DC 那些.v 文件可能的路径,让 DC 根据.v 文件的名字去找.v 文件。

如:set_app_var search_path “$search_path ./../....libs”;

set_app_var search_path [list . $xxx $xxxx ](注意:换行的时候需要

加换行符\,而且\后面不能有空格),list 是列表的意思,意思是这个

search_path 有多个(路径)变量,第一个路径变量搜索不到(库或者需要

的文件),可以从下一个路径中找;中间的 . 的意思好像是当前目录。

接下来就是指定库文件了。在前面粗略地说了一下各个库的意思,下面在介

绍指定库的同时,也接收这些库在 DC 中的作用,但是库里面的具体内容不

在下面说明,将在后面进行介绍。

⑩④指定综合库(synthetic_library):这个库一般是 synopsys 的库:

DesignWare library 和标准单元库,这里指定的就是库的名字了。

DesignWare library 这个库是 synopsys 的 IP 库:当使用到 synopsys 公


司的 IP 核的时候比如使用了该公司的乘法器 IP,那么就要定义这个综合库;

此外,当需要用到这个库的一些比较高端的 IP 核的时候,是需要相关的证

书的。

例如当你在代码中用来“*”或者“+”这操作,那么 DC 会通过这个你指定

的综合库进行映射或者优化这些操作符(就是翻译成门电路)。这个库非必

须指定。在这些库的名称如下所

⑩⑤指定目标工艺库(target_library):DC 将 RTL 级的 HDL 描述映射到实

际的门级电路时所需要的标准单元库。这个库指定为半导体制造商提供的工

艺库。库里面的具体内容后面详细说明。这里指定的就是库的名字。
附注:启动 DC 之后,可以通过 printvar target_library 查看工艺库名称。

⑩⑥指定链接库(link_library):这里指定的就是库的名字了,链接库可以

从下面几点理解:

 link_library 是 target_library 一样的单元库或者是已经综合到 Netlist 的底

层模块设计(比如 IP 核)。作用是:用于分辨电路中逻辑门和子模块的功

能,然后用实际的库单元或者子模块代替它们;在由上而下的综合工程中,

上一层的设计调用底层已经综合的模块时,将从 link_library 中寻找并且链

接起来,因此当读入的文件是门级网表(比如用到了 IP 核的网表)时,需

要把 link library 设成指向生成该门级网表的目标库,否则 DC 因不知道

网表中门单元电路的功能而报错。

如果需要将已有的设计从工艺 A 转到工艺 B 时,可以将当前的单元综合库 A

设为 link_library,而将单元综合库 B 设为 target_library,重新映射一下就

可以了。

 路径面前加*号表示开辟一块单独的内存空间给 DC 自己使用,然后先搜寻

内存中已有的库,然后再搜寻变量 link_library 指定的其他库。DC 搜寻的

库为 search_path 指定的目录(比如说之前读入设计时读入了库 a,库 a 存

到内存里;这时 DC 在进行综合的时候,发现缺少某个东西,于是就先从库

a 里面找,找不到时就会从列表里面的变量路径中找)。

 一般情况下,我们只用一个工艺库,需要引用目标工艺库,因此指定

target_library;此外我们还有可能用到 synopsys 公司的 IP 核,因此需要

指定他的 DW 库。(这一点也就是上面这么指定 link_library 的原因)。

⑩⑦最后是指定图形化符号库(symbol_library):这里指定的就是库的名字了,
这个前面说得比较详细了,这里不再重述。

⑩⑧最后 source 的那个脚本是用来定义一些命名规则,去掉网表中的一些符号,

防止后端工程师拿到的网表中带一些奇怪的符号从而引起不必要的错误。

注意:

·库的指定只需要指定 target_library、link_library,其他的可以不指定;

·上面的库设置仅仅适用于逻辑综合,也就是非拓扑模式下的综合;拓扑模

式下,需要使用到物理库,或者相关的物理布局等信息;对于拓扑模式下的综合,

我们在前面的流程中提及到,在后面的实验中,我们也会进行相关的解释。

·最后,一般情况下(以后的文章中),为了方便管理,我们会像上一节那样:

A 路径变量的定义我们会放在 common_setup.tcl 中(一般是用 set 设置的

变量)。

B 库变量的指定我们一般放在 dc_setup.tcl 这个文件中,这里的库变量一般

是用 set_app_var 进行指定,此外指定的是具体的某一个库,而不仅仅是路径,

DC 会从 search_path 里面寻找到这些库。

C.synopsys_dc.setup 文件中,仅仅包含 source xxx.tcl 这些内容,比如

source common_setup.tcl ;source dc_setup.tcl 以及 source 其他的设置

内容。
五、综合库(时序库)和 DC 的设计对象
前面一直说到综合库/工艺库这些东西,现在就来讲讲讲综合库里面有什么东西,

同时也讲讲 synopsys 的 Design Ware 库。主要内容分为三个部分:

 标准单元库

 DC 的设计对象

 Design Ware 库

1、标准单元库

绝大多数的数字设计流程都是基于标准单元的半定制设计流程。标准单元库

包含了反相器、缓冲、与非、或非、与或非、锁存器、触发器等等逻辑单元

综合模型的物理信息,标准单元是完成通用功能的逻辑,具有同等的高度(宽

度可以不同),这样方便了数字后端的自动布局布线。

1 概述

一个 ASIC 综合库包括如下信息:

 一系列单元(包括单元的引脚)。

 每个单元的面积(在深亚微米中,一般用平方微米表示,在亚微米工艺

下,一般用门来称呼,至于具体的单位,可以咨询半导体制造商)。

 每个输出引脚的逻辑功能。

 每个输入到输出的传递延时,输出到输出的传递延时;inout 到输出的

传递延时。

2 内容与结构

Synopsys 的工艺库是一个.lib 文件,经过 LC 编译后,产生.db 文件。工艺

库文件主要包括如下信息:
·单元(cell)(的信息):主要有功能、时间(包括时序器件的约束,如建

立和保持)、面积(面积的单位不在里面定义,可按照规律理解,一般询问半导

体厂商)、功耗、测试等。

·连线负载模型(wire load models):电阻、电容、面积。

·工作环境/条件(Operating conditions):制程(process)(电压和温度

的比例因数 k,表示不同的环境之间,各参数缩放的比例)

·设计规则约束(Design ):最大最小电容、最大最小转换时间、最大最小

扇出。

工艺库的结构如下:

文本描述如下所示:
我使用的 TSMC90nm 的工艺库,我用 slow.lib 这个库来给大家介绍:

这个库总共三万多行,不可能每一行都解说,因此我按照结构进行介绍。

打开这个.lib 文件,可以看到最前面:

最前面的是这些注释,描述的是:制程(是慢的模型)、电压、温度等数据信息。

接下来才是真正的库的信息:

库组(大结构):

Library(library_name){
......

......

A 首先是库的属性的描述:

下面是这张图的解释:

 通用属性描述(general attribute)

主要是工艺类型、延迟模型、替代交换方式、库特征、总线命名方式等信息

工艺类型:这个库没有给出,主要用来说明这个库是 CMOS 工艺还是 FPGA

工艺。默认是 CMOS 工艺。

延迟模型:指明在计算延迟时用的那个模型,主要有 generic_cmos(默认值)、

table-lookup(非线性模型)、piecewise-cmos(optional)、dcm(Delay

Calculation Module)、polynomial。这个库使用的非线性模型。

替代交换方式:这里选的是匹配封装的方式。具体的信息可以查阅其他治疗

或者询问半导体厂商。

库特征:报告延迟计算,也就是这个库具有延迟计算的特征。
总线命名方式:定义库中总线命名规则。例如:

bus_naming_style:"Bus%spin%d";这个库没有进行总线规则的命名。

 库的文档资料属性(document attribute)

主要是库的版本、库的日期、还有注释。例如:

用库报告命令 report_lib 可显示日期例如:Date:"Wed Jun 22 12:31:54

2005"。

修正版属性定义库的版本号码,例如 Revision:1.3;

注释属性用于报告 report_lib 命令所显示的信息,如版权或其他产品信息。例

如:Comment:"Copyright (c) 2005 Artisan Components, Inc. All

Rights Reserved.”

 定义单位属性(unit attribute)

Design Compiler 工具本身是没有单位的。然而在建立工艺库和产生报告时,

必须要有单位。库中有 6 个库级属性定义单位:time_ unit(时间单位)、

voltage_unit(电压单位)、current_ unit(电流单位)、

pulling_resistance_unit(上/下拉电阻单位)、capacitive_load_unit(电容负

载单位)、leakage_power_unit(漏电功耗单位)。

单位属性确定测量的单位,例如可在库中用毫微秒(nanoseconds)或皮法拉

(picofar-ads)作为时间和电容负载的单位。注:关于面积的单位,前面已经

说了,这里不再详述。

B 接下来是环境描述:

主要包括操作条件(operation conditions)、临界条件定义(threshold

definitions)、默认的一些环境属性(default attributes)、一些(时序、
功耗)模型(templates)、比例缩放因子(k-factors)、I/O pad 属性(pad

attributes)、线负载模型(wire-loads)。

 操作条件(operation conditions)

在工艺库中,用操作条件设置了制程(process)、温度(temperature)、

电压(voltage)与 RC 树模型(tree_type)。

在综合和静态时序分析时,DC 要用到这些信息来计算电路的延迟,而库中

的这组操作条件为基础(也就是 nom_xxxx)操作条件。一个工艺库只有这

么一组基础的操作条件,如果要使用不同的操作条件,则需要借助 K 参数了

(见后面)。制程、温度、电压这些很好理解,下面主要说一下这个 RC 树

模型(tree_type)。

tree-type 属性定义了布局之前延时的计算方式。此外,wire_load_model

线负载模型(后面有讲)是根据连线的扇出来估算连线的 RC 寄生参数的,

RC 如何分配就是根据这个 tree-type 属性来的。

连线延时(从驱动引脚的状态变化到每个接受单元输入引脚的状态变化,线

负载模型设每个分枝的延迟是一样的。)的一个示例如下图所示:
在这个简单的电路中,BUF1 的输出驱动两个单元:BUF2 与 BUF3。在物理

上,这是两根连线。而在网表中,两根连线用一个 net 来表示。

在布局前,假设这两根线有相同的寄生电阻与寄生电容,即

Cwire1-Cwire2=R1-R2 。

假设我们想了解从 BUF1 的输出到 BUF2 的输入端的延时。这个延时实际上

是给连线及 BUF2 的输入引脚负载进行充、放电所消耗的时间。

如何计算这个延时呢?tree-type 就是为此而定义的。Tree-type 有三种取值,

这个延时就有三种计算模型,这三种模型有两种理解方式,这两种理解方式

是等价的。

第一种理解方式的三种模型:

A:当它取值为 worst-case-tree 时,连线的寄生参数采用集总模型,即用

Cwire*(Cwire 1+Cwire2)这个乘积表示连线的等效电容,Rwire(R1+R2)

表示连线的等效电阻。C1 表示 BUF1 输入引脚的等效电容。C2 表示 BUF2

输入引脚的等效电容。从 BUF 1 到 BUF2 的延时计算模型下图所示:


在这种模型中,net 本身的延迟为 Rwire*Cwire .

B:当 tree-type 取值为 best-case-tree 时,计算延时的 RC 模型如下图所示:

在这种模型中,Rwire 为 0,因此 net 本身的延时为 0

C:当 tree-type 取值为 balanced-tree 时,计算延时的 RC 模型如下图所

示:

在这种模型中,net 的延时为 Rwire*Cwire/(N^2)。这里 N 表示负载数目,

本例中取值为 2.

第二种理解方式的三种模型:

无论是从哪一种方式理解,这个库中使用的是平衡树的模型。
 临界条件定义(threshold definitions)

主要是定义一些极限值,比如时钟抖动的最大最小值、输出输出的上升下降

沿的最大最小值等等信息,如下图所示:

 默认的一些环境属性(default attributes)

主要是默认漏电流功耗密度、标准单元的漏电流功耗、扇出负载最大值、输

出引脚的电容、IO 类型的端口电容、输入引脚的电容、最大转换时间。

 一些(时序、功耗)模型(templates)
都是写查找表模型,主要是功耗(比如输入转移时间的功耗)、时序(比如

输入线转换的延时、建立时间和保持时间的延时)等等,根据不同的操作环

境,进行查表进行选择对应的参数。

 比例缩放因子(k-factors)

由于一般库中只有单元“nom_xxx”的值,为了计算不同的制程、电压和温

度下单元的延迟(或者说是计算不同的操作条件),库中提供了比例缩放因

子:

比例因子有许多,这里只是列举了这几个。

根据提供的 K 参数,DC 按下面的公式计算不同的制程,电压和温度的单元

延迟:

Delay derated = (nominal delay)*

(1+(DP*KfactorP))*(1+(DV*KfactorV))*(1+(DT*KfactorT))

其中:

delta = current-nominal ;

DP=CP-NP,CP 为 current process,NP 为 nominal process;

DV=CV-NV,CV 为 current voltage,NP 为 nominal voltage;

DT=CT-NT,CT 为 current temperature,NT 为 nominal temperature.


KfactorP、KfactorV、KfactorT 分别是对于的 K 参数,表示制程、电压、

温度对延时的影响。

 I/O pad 属性(pad attributes)

主要就是定义 I/O 引脚的电平属性,告诉你输入是 COMS 还是 TTL,什么

时候达到高电平、什么时候是低电平。

 线负载模型(wire-loads)

工艺库的线负载模型如下所示:
DC 采用 wire-load 模型在布局前预估连线的延时。通常,在工艺库中,根

据不同的芯片面积给出了几种模型(上图所示)。这些模型定义了电容、电

阻与面积因子。此外,导线负载模型还设置了 slope 与 fanout_length,

fanout-length 设置了与扇出数相关的导线的长度。

有时候,除了扇出与长度,该属性还包括其他参数的值(这个工艺库没有),

例如 average_capacitance、standard_deviation 与 number_of_nets,在

DC 产生导线负载模型时会自动写出这些值。对于超过 fanout-length 属性

的节点,可将该导线分成斜率不同的几段,以确定它的值。

C 工艺库剩下的全是标准单元(cell)的描述:如反相器、触发器、与非门、

或非门的描述等:

 标准单元内容概述

综合库中的每个单元都包括一系列的属性,以描述功能、时序与其他的信息。

在单元的引脚描述中,包含了如下内容:输入引脚的 fanout-load 属性、输

出引脚的 max_fanout 属性、输入或输出引脚的 max_transition 属性、输

出或者 inout 引脚的 max_capacitance 属性。利用这些描述,可以对设计

进行 DRC(设计规则检查)。如果某个单元的输出最大只能接 0.2pF 的负载,


但在实际综合的网表中却连接了 0.3pF 的负载,这时候综合工具就会报出

DRC 错误。

通常,fanout_load 与 max_fanout 一起使用 max_transition 与

max_capacitance 一起使用。 如果一个节点的扇出为 4,它驱动 3 个与非

门,每个与非门的 fanout-load 是 2,则这三个与非门无法被驱动(因为

3*2>4)。

max_transition 通常用于单元的输入引脚,max_capacitance 一般用于单

元的输出引脚。如果任何节点的 transition 时间大于引脚的 max_transition

值,则该节点不能连接。如果发生违例,则 DC 用一个具有更大

max_capacitance 值的单元来取代驱动单元。

在对输出引脚的描述中,给出了该引脚的功能定义,以及与输入弓}脚相关的

延时。输入引脚定义了它的引脚电容与方向。这个电容值不能与

max_capacitance 值相混。DC 利用输入引脚的电容值进行延时计算,而

max_capacitance 仅用来进行设计规则检查。

对于时序元件中的时钟引脚,专门用 clock 类型进行说明,如下所示:

注:许多设计者都会抱怨工艺库中对单元的 DRC 属性设置不当,这是由于库

的能力是有限的所致。对于一个设计,综合库的 DRC 设置可能很合适,而

对于另一个设计就可能不太合适。这时候,需要设计者对综合库进行“剪裁”。
当然,这种“剪裁”必须比库中的定义更为严格。如将一个库中 buffd0 的

Z 端的 max_fanout 由 4.0 改为 2.0 的命令:

dc_shell> set_addribute find(pin,ex25/BUFFDO/Z) max_fanout 2.0

上述的命令可以写在 synopsys-dc.setup 文件中。

单元的延时--在一个单元的综合库中,最核心的是对时序和功耗的描述。一

个单元的延时跟以下因素有关:

器件内部固有的延时、输入转换时间(也称为输入上升/下降时间)、负载(驱

动的负载及连线)、温度、电压、制程变化。

前三个因素是由电路本身的特性所决定的,后三个因素是由环境决定的。在

实际电路中,输入转换时间、负载与连接单元的电路有关,所以我们只需要

列出在不同的输入转换时间、不同的负载下单元的延时就可以了。这个延时

包括器件的内部固有延时。此外,利用前面提到 K 缩放因子,将温度、电压、

制程的影响也考虑进来。如下面的这个反相器单元,它的延时就可以通过输

入转换时间和负载决定:

说到单元的延时,不得不说计算单元延时的模型。

Synopsys 支持的延时模型包括:CMOS 通用延时模型、CMOS 分段线性延

时模型和 CMOS 非线性延时查找表模型(Nonlinear Delay Model)。前

两种模型精度较差,已经被淘汰,主要用非线性延时模型。下面进行解释非
线性延时模型。

非线性延时模型也称为二维非线性延时模型。在该模型中,用二维列表的形

式给出单元在特定的输入转换时间、输出负载下的延迟(包括单元的延时和

单元的输出转换时间):

单元的输出转换时间又成为其驱动的下级单入的输 ru 转换时间。库中每个

单元有两个 NLDM 表。上面的图中,当输出负载和输入转换时间为 0.05 pF

和 0.5 ns 时,从表中可查出单元的延迟为 0.23 ns,输出转换时间为 0.30

ns 。

对于在范围之内的点,可以用插值的方法得到;对于在范围之外的点,可以用

外推的方法得到。线性插值如下图所示:

计算延时的公式为:
Z=A+BXX+CXY+DXXXY

其中,Z 代表单元的延时,A, B, C, D 是系数,X 为输出节点电容,Y 为输入

转换时间。

输入的上升、下降时间是由上一级输出的上升、下降时间得到的。输出节点

的电容可以由负载的输入引脚电容及连线负载计算得到。在综合时,使用导

线负载表可以预测导线负载。导线负载模型在综合库中进行了定义。当然,

用户也可以自己生成连线负载模型。该模型也是用查找表的方式,列出在不

同负载下的平均连线延迟。在布局之后,可以得到更为精确的导线长度。在

布线后,可以得到最确切的导线长度。可以用该导线负载来计算最终的延时,

以便进行静态时序分析与时序计算。

使用线性插值的举例:一个标准单元的延迟查找表如下图所示:

在查找表中,根据不同的输入转换时间和输出节点电容,列出了标准单元的

延时。例如,当输入节点的转换时间为 0.1 ns,输出负载为 0.01pF 时,单

元的延时为 0.17 ns。如果单元的输入转换时间为 0.2 ns,输出节点电容为

0.002 pF,则从表中无法直接查找到延时,需要通过线性插值的方法来求得

该值:

计算延时的第一步,是求出延时公式中的系数 A, B, C, D,然后根据实际

的输入转换时间和输出电容求出实际的延时。

假设该单元的输入转换时间为 0.2(位于查找表的第 1 列与第 2 列之间),输


出节点电容为 0.003(位于查找表的第 1 行与第 2 行之间),这样,我们将查

找表中的相应的 4 值代入延时公式,可得:

0 .080=A+B*0.1 +C *0.001 +D*0.1*0.001;

0 .130=A+B * 0 .5+C * 0.001 +D*0.5*0.001;

0 .170=A+B * 0 .1+C*0.01 +D*0.1*0 .01

0 .220=A+B * 0.5+C*0.01 +D*0.5*0.01

求解这 4 个等式,可得 A=0 .057 52,B=0 .1248,C=9 .9998,D=0 .2。

接下来,我们将实际的节点电容、输入转换时间代入延时公式,可以得到这

种情形下该单元的实际延时为:

Z=0 .5752+0.1248X0.003+9.9998 X 0.2+0.2X0.2X0.003 (ns)

单位为 ns,输出转换时间、短路功耗的计算与此类似。

说明:利用 DC 中的 report_power_calculation 命令,可以显示出某一节点

处 energy 的具体求解过程;利用 DC 中的 report_delay_calculation 命令,

可以显示出某一节点处延迟或输出转换时间的具体求解过程。

前面对标准单元库的内容和结构有了一个概述,下面对一个反相器单元和一

个寄存器单元进行详细解说。

 反相器的综合模型

综合库中主要给出了各端口的功能、电容、功耗及延时等信息(不同的库模

型信息种类可能不一样)。反相器的功耗主要分为两部分:静态功耗和动态

功耗。 其中静态功耗是指泄漏功耗,动态功耗包括翻转时的短路功耗及节

点电容的充放电所消耗的功耗。节点电容充放电消耗的功耗仅跟 VDD、节

点翻转率及节点电容有关。其中,VDD 和节点电容是固定的,节点翻转率
跟输入激励有关,需要通过仿真激励进行计算。因此,在综合库中,不列出

充放电功耗。而短路功耗跟输入转换时间和节点电容有关,一般以查找表的

形式给出(在综合库中,用 internal_power 表示)。

具体的反相器的综合模型如下所示:

当然,上面看到的只是部分内容,有些内容折叠起来了比如输入引脚的等效负载、

输出引脚的短路功耗等被折叠起来了,下面进行讲解:

引脚 A 表示反相器的输入(上图),展开后可以看到输入的引脚的等效负载,

也就是等效电容的大小。

输出管脚

里面主要包含了引脚的功能(function)、短路功耗(internal_power)、时序
信息(timing)、输出的最大负载(max_capacitance)等信息。

短路功耗信息(internal_power):

展开短路信息如下图所示:

短路功耗与 A 管脚相关联,也就是与 A 管脚有关系。

rise_power 给出了 Y 从低到高时的短路功耗,功耗跟输入信号的转换时间

(index_1)及节点电容(index_2)有关;根据不同的信息进行查表,表的值

就是(value),7X7 表示 index_1 是 7,index_2 也是 7,因此 value 是 7X7=49,

如下图所示:

也给出了从高到低的短路功耗(fall_power),功耗跟输入信号的转换时间及节

点电容有关;具体内容类似从低到高时的短路功耗,不再详述。

时序信息(timing):

主要包括相关引脚、时序敏感类型、单元延时、转换时间等。
Cell_rise 给出了 Y 从低到高时单元的延时,延时跟输入转换时间和节点电容有

关:

Rise_transition 给出 Y 从低到高时输出的延时(transtion),跟输入转换时间和

节点电容:

Cell_fall、fall_transition:则是对应 Y 从高到低时的单元延时和输出延时,也

是跟输入转换时间和节点电容有关。

cell_leakage_power:单元的泄漏功耗,有时候,也给出在不同条件时的泄漏

功耗(也就是查表的方式):

反相器作为组合逻辑最简单、最经典的模型,其综合库的模型内容就如上所示了,

下面介绍时序逻辑的。
 寄存器单元的综合模型

寄存单元综合库模型主要包括以下内容:

 单元面积;

 D 端短路功耗;

 D 端的建立时间约束和保持时间约束;

 CK 时钟引脚上的短路功耗;

 CK 时钟引脚上的最小脉宽要求;

 Q 输出端的短路功耗;

 Q 输出端的延时;

 Q 输出端的最大负载约束;

 单元的泄漏功耗

其综合库内容如下所示:
上面综合库的具体内容下面进行具体叙述:

D 端口(引脚):

输入端口,主要包含输入的等效负载、短路功耗、时序信息。

internal_power,D 端消耗的短路功耗:

rise_power 给出 D 端由低电平变到高电平时的短路功耗,跟输入转换时间有关,

电容为定值:

fall-power 则给出 D 端由高电平变到低电平时的短路功耗,跟输入转换时间有

关,电容为定值。

Timing,时序信息:

D 的时序信息与“CK”端口有关,Setup_rising 表明是 setup(建立时间)的

时序信息。
rise-constraint:给出 D 端由低电平变到高电平时的 setup 约束,跟 D 端输入

转换时间和时钟的转换时间有关:

fall-constraint 则给出 D 端由高电平变到低电平时的 setup 约束,跟输入转换

时间和时钟的转换时间有关。

前面我们看到,有两个 timing,一个既然是建立时间了,那么另外一个就是保

持时间了:

rise-constraint:给出 D 端由低电平变到高电平时的 hold 约束,跟输入转换时

间和时钟的转换时间有关:

fall-constraint:给出 D 端由高电平变到低电平时的 hold 约束,跟输入转换时

间和时钟的转换时间有关。

CK 端口:
这个是时钟端口,主要给出了输入的负载、最大转换时间约束、短路电流、高低

电平的最小脉宽要求,如下图所示。

Internal_power,短路功耗:

也是分为 CK 上升时跟下降时的短路功耗,与 D 端有关。

min_pulse_width_high、min_pulse_width_low :给出了时钟高低电平的最

小脉宽要求。

ff:描述寄存器的功能。

Q 端口:
主要描述 Q 的功能,短路功耗,时序信息,输出最大的负载电容。

Internal_power,短路功耗:

rise_power 给出了输出端由低电平变到高电平时的短路功耗,跟输入转换时间、

Q 端负载及 QN 端负载有关:

fall_power 则输出端由高电平变到低电平时的短路功耗,跟输入转换时间、Q

端负载及 QN 端负载有关。

Timing,时序信息:
说明 Q 端口与 CK 的上升沿有关。

cell-rise 给出 Q 端由低电平变到高电平时 CK 到 Q 的延时,跟输入转换时间和

输出节点电容有关:

rise-transitio 给出了 Q 端由低电平变到高电平时 Q 端转换时间,跟输入转换时

间和输出节点电容有关:

cell-fall 给出了 Q 端由高电平变到低电平时 CK 到 Q 的延时,跟输入转换时间

和输出节点电容有关;

fall-transition 给出了 Q 端由高电平变到低电平时 Q 端转换时间,跟输入转换

时间和输出节点电容有关。

max_capacitance,则是最大输出负载电容约束。
QN 管脚,跟 Q 管脚类似,不进行陈述了。

cell_leakage_power 给出了默认单元漏电流大小。而下面的不同漏电流,则是

根据端口信号处在不同状态时的漏电流大小

注:对于有复位信号的寄存器,还有可能有下面的信息:

· 复位引脚上的短路功耗;

· 复位引脚上的最小脉宽要求;

· 复位引脚上的 recovery/removal 时序约束;

· 输出端的输出转换时间(相对于时钟);

· 输出端的输出转换时间(相对于异步复位信号);

· 输出端的延时(相对于异步复位信号);

2、DC 的设计对象

在了解了综合库之后,下面介绍一下 DC 的设计对象,虽然这个设计对象相对于

综合库没有那么重要,但是还是要了解一下的。

对于一个 verilog 代码模块,我们知道这是一个模块的名字是什么,这个模块的

功能是什么,这个模块有哪些端口等等信息。但是对于 DC 来说,它不像我们那

么理解,给它一个 verilog 模块,它把这个模块的内容当做设计对象(简称对象)


来看。DC 支持的对象和解释如下所示:

DC 支持 8 中设计对象:

 Design : 具有某种或多种逻辑功能的电路描述;

 Cell : 设计的 instance;

 Reference : cell 或 instance 在库中定义的名字;

 Port : design 的输入、输出;

 Pin : design 中 cell 的输入、输出;

 Net : ports 和 pins 之间或 pins 之间的信号名;

 Clock : 被定义为时钟源的 pin 或 port;

 Library : cell 的集合,如:target_library,link_library;

在 DC 读入设计时候,可以通过下面命令查看这些对象:
Query:访问某一个对象,

Sizeof:查某一个(对象)集合的大小。

对象具有某些属性,比如:

端口(port)的属性有:方向、驱动单元、负载、最大电容约束等等

单元(cell)的属性有:层次化、不触碰 等待;

时钟的属性有:周期、抖动等;

写约束,就是通过对设计对象的属性进行约束,至于要约束什么,怎么约束,在

后面进行介绍。

3、Design Ware 库

DesignWare 是 Synopsys 提供的知识产权(Intellectual Property,简称 IP)库。

IP 库分成可综合 IP 库(synthesizable IP,SIP) ,验证 IP 库(Verification IP,VIP)

和生产厂家库(foundry libraries)。IP 库中包含了各种不同类型的器件。这些器

件可以用来设计和验证 ASIC, SoC 和 FPGA。库中有如下的器件:


 积木块(Building Block)IP(数据通路、数据完整性、DSP 和测试电路等等)

 AMBA 总线构造(Bus Fabric)、外围设备(Peripherals)和相应的验证 IP

 内存包(Memory portfolio)(内存控制器、内存 BIST 和内存模型等等)

 通用总线和标准 I/O 接口(PCI Express,PCI-X,PCI 和 USB)的验证模型

 由工业界最主要的明星 IP 供应商提供的微处理器和 DSP 核心

 生产厂家库(Foundry Libraries)

 板级验证 IP<Board verification IP)

 微控制器(Microcontrollers,如 8051 和 6811)

这里主要介绍集成在 DC 综合工具中的 designware foundation 库。所有的 IP

都是事先验证过的、可重复使用的、参数化的、可综合的,并且不受工艺的约束。

常用的 DesignWare foundation 库单元如下所示:


使用 IP 库中的器件,可以用运算符号推论法(Operator Inferencing)或功能推论

法(Functional Inferencing)。运算符号推论法是直接在设计中使用“+、一、*、>、

一和<”等的运算符号。功能推论法是在设计中例化(instantiate) DesignWare

中某种算术单元,例如直接指定用库中的 DWF_ mult_ tc,DWF_ div_ uns 和

DWF_sqrt_tc 单元。

由于 DesignWare 库中的所有器件都是事先验证过的,使用该 IP 库我们可以设

计得更快,设计的质量更高,增加设计的生产力和设计的可重复使用性,减少设

计的风险和技术的风险。对于每个运算符号,一般地说 DesignWare 库中会有

多个结构(算法)来完成该运算。这样就允许 DC 在优化过程中评估速度/面积的折

衷,选择最好的实现结果。对于一个给定的功能,如果有多个 DesignWare 的

电路可以实现它,Design Compiler 将会选择能最好满足设计约束的电路。此

外使用 DesignWare 中的 DW Foundation 库是需要许可证的(license) , DW

Foundation 库提供了更好的设计质量(Quality of Result)。

使用 DesignWare 中 IP 的方法如下图所示:

Design Compile 自动选择和优化算术器件。对于算术运算,我们并不需要在

DC 中指定标准的(基本的)综合库 standard.sldb。标准的综合库 standard.sldb


包含内置的 HDL 运算符号,综合时 DC 会自动使用这个库。如果我们要使用性

能更高的额外的 IP 库,例如 DW_ foundation.sldb,我们必须指定这些库,如

下所示:

#Specify for use during optimization

set synthetic_library dw_foundation.sldb

#Specify for cell resolution during link

lappend link_library $synthetic_library

本文的总结主要参考了《专用集成电路设计使用教程》、《数字 IC 系统设计》,

局部图片来自这两本书。
六、基本的时序路径约束
时序约束可以很复杂,这里我们先介绍基本的时序路径约束,复杂的时序约束我

们将在后面进行介绍。

在本节的主要内容如下所示:

 时序路径和关键路径的介绍

 建立时间、保持时间简述

 时钟的约束(寄存器-寄存器之间的路径约束)

 输入延时的约束

 输出延时的约束

 组合逻辑的约束

 结合设计规格进行实战

RTL 代码描述了电路的时序逻辑和组合逻辑,即 RTL 代码体现了电路的寄存器

结构和数目、电路的拓扑结构、寄存器之间的组合逻辑功能以及寄存器与 I/O 端

口之间的组合逻辑功能。但代码中并不包括电路的时间(路径的延时)和电路面积

(门数)。综合工具现在不能很好地支持异步电路,甚至不支持异步电路,因此时

序路径的约束主要是针对同步电路的,关于异步的电路的约束,后面也会进行相

关的说明。

1、时序路径与关键路径

我们来看一下同步电路,常见的结构如下所示:
中间是我们设计的模块(芯片),对于同步电路,为了使电路能正常工作,

即电路在我们规定的工作频率和工作环境下能功能正确地工作,我们需要对

设计中的所有时序路径进行约束。那么时序路径是什么呢?下面进行解说:

时序路径是一个点到点的数据通路, 数据沿着时序路径进行传递。每条时

序路径有一个起点(Startpoint)和一个终点(Endpoint)。

起点定义为:

 输入端口

 触发器或寄存器的时钟引脚:CK

终点定义为:

 输出端口

 时序器件的除时钟引脚外的所有输人引脚:D DQ

这样, 时序路径可以是输入端口到寄存器、 寄存器到寄存器、 寄存器到

输出端口、 输入端口到输出端口。如下面这个电路中:

就有 4 条路径:
1:从输入端口 A 到 FF1 的 D 端;

2:从 FF1 的 CLK 端到 FF2 的 D 端;

3:从 FF2 的 CLK 端到输出端口 out1;

4:从输入端口 A 到输出端口 out1。

好看一点的图如下:

路径的特性是存在延时,也就是说,路径 1、2、3、4 都存在有延时,延时

最长的一条路径称为关键路径。一般情况下,路径 1、2、3 是最常见的,路

径 4 比较少见。

2、常见的时序路径约束

①建立时间、保存时间和亚稳态

在进行约束的时候,先了解触发器的三个概念:建立时间、保持时间以及亚

稳态。这里只是简单地介绍一下,关于建立时间和保持时间的深入介绍,请

查看我的博文:http://www.cnblogs.com/IClearner/p/6443539.html,

关于亚稳态的深入介绍,请查看我的博文:

http://www.cnblogs.com/IClearner/p/6475943.html

建立时间:时钟有效沿到来之前的某段时间内,数据必须稳定,否则触发器

锁存不住数据,这段时间成为建立时间,用 Tsetup 或者 Tsu 表示。


保持时间:时钟有效沿到来之后的某段时间内,数据也必须稳定,否则触发

器锁存不住数据,这段时间成为保持时间,用 Thold 或者 Th 表示。

如下图所示:

在第二个时钟上升沿的时候,要锁存住输入端 D 的高电平,D1 是满足了建

立时间和保持时间的情况;而 D2 则是建立时间没有满足,因此不能成功锁

存住输入的高电平;D3 保持时间不满足,也不能成功锁存输入的高电平。

亚稳态:每个触发器都有其规定的建立(setup)和保持(hold)时间参数,该参

数存放在由半导体厂商所提供的工艺库中。假如触发器由时钟的上升沿触发,

在这个时间参数内,输入信号是不允许发生变化的。否则在信号的建立或保

持时间中对其采样,得到的结果是不可预知的,有可能是 0 或者 1,即亚稳

态。

有了这三个概念之后,我们可以对路径进行约束了。约束就是为了满足寄存

器的建立时间(和保持时间),我们先对模块内的路径进行约束,也就是下面

电路框图中的中间部分:
对于中间的部分路径,可以用前面的那个路径图来描述:

也就是主要约束这些类型的路径,本小节主要讲的就是这些路径的约束。

②路径 2(寄存器到寄存器之间的路径)的约束:

我们先从寄存器到寄存器之间的路径 2 开始;前面说到了,为什么要约束时

序路径,是为了满足寄存器的建立时间和保持时间。对于路径 2,数据从 FF1

的 D 端口传输到 FF2 的 D 端口,主要需要经历触发器的翻转时间/转换延时、

寄存器与寄存器之间的组合逻辑延时、连线延时这些种延时。因为数据是随

着时钟的节拍一拍一拍往后传的,因此这里的寄存器与寄存器之间的路径约

束,就是对时钟的建模,或者是说对时钟的约束。下面进行说明:

为了满足 FF2 建立时间的要求,也就是数据经过上面的延时(触发器的翻转

时间/转换延时、寄存器与寄存器之间的组合逻辑延时、连线延时)之后到

达 FF2 的 D 端的时间再加上 FF2 的建立时间,需要要小于时钟周期;也就

是说,你的那些延时不能过大,延时一旦过大,数据可能就不满足建立时间
的关系,甚至还更新不了。举个例子说:

现在的节拍(0ns 这一时刻)到来后,数据(比如说是一个高电平)从 FF1

的 D 端传来,经过组合逻辑,在下一个节拍(20ns 这一时刻)的时候传到

FF2 的 D 端,更新 FF2 的数据(0ns 时,FF2 保存的是低电平),如红色箭

头所示;但是由于延时太大,下一个节拍到来了(20ns 到了),这个高电

平还在还在组合逻辑那里,如绿色箭头所示,就导致了 FF2 的 D 端数据不

能得到更新,或者不满足建立时间,由此可能引起锁存错误。当对时钟进行

建模之后,拍长也就决定了,也就是那些延时(触发器的翻转时间/转换延

时、寄存器与寄存器之间的组合逻辑延时、连线延时)最大是多少也就知道

了,通过对时钟进行建模,也就是通过对寄存器与寄存之间的路径进行约束,

DC 就知道了这条路径运行的最大延时,就会选择合适的单元来满足这些延

时的约束,如果 DC 选来选去,发现最牛逼的单元得到的电路延时还是很大,

无法满足 FF2 的建立时间要求,DC 就会报错。这个时候,你就要进行修改

设计了(比如修改约束或者修改代码)。

为了满足 FF2 的保持时间,也就是数据经过上面的延时(触发器的翻转时间

/转换延时、寄存器与寄存器之间的组合逻辑延时、连线延时)之后到达 FF2

的 D 端的时间,不能小于某个值。也就是说,这些延时也不能太小。举个极
端的例子说,在 0ns 的时候,触发器有效沿到来,FF1 和 FF2 都要更新下数

据,FF1 准备锁存高电平,FF2 准备锁存低电平;由于 FF1 反应很快,电路

延时很小,FF1 寄存住的高电平很快就会传到 FF2 的 D 端口,高电平就会冲

掉 FF2 要锁存的低电平(也就是 FF2 的 D 端口低电平还没有稳定,违反了

保持时间),也就是说会导致低电平对 FF2 的保持时间不能得到满足,导致

FF2 更新数据失败,要锁存住的低电平可能就产生亚稳态。因此就有传输延

时需要大于 FF2 的保持时间。此外,我们由此也可以知道,保持时间的分析

比建立时间的分析提前一个时钟周期沿,也就是说在 0ns 时候传输数据,建

立时间是在下一个时钟上升沿(20ns 时刻)进行检查 FF2 的 D 端口数据是

否稳定(若不稳定,就违反了建立时间),而保持时间是在发送数据的同一

时刻(也就是 0ns 时刻)检查 FF2 的 D 端口数据是否稳定(如果不稳定,

就违反了保持时间);关于保持时间的分析比建立时间的分析提前一个时钟

周期沿这一点需要注意。

然而,保持时间一般是能够满足的,也就是传输延时一般是大于触发器的保

持时间的,即使满足不了,在后端版图设计的时候,也可以有修改措施(比

如路径加缓冲器增加延时)。因此我们在约束的时候,我们一般不关注保持

时间,而是注重建立时间。

经过上面一大堆的废话,相信大家已经对这个约束过程有一定的了解了,下

面进行概括一下,并进行时钟建模。

通过上面的讲解,我们知道,一般情况下,如果寄存器和寄存器之间组合电

路的延迟大于 Clock_cycle-Tsu(Clock_cycle 是时钟周期,Tsu 是触发器的

建立时间),电路的功能会不正确,将不能正常工作。如果已知电路的时钟工
作频率,就知道了寄存器和寄存器之间组合电路的最大时延,如下图所示:

图中路径 X 的最大时延应满足下列关系:

Tclk-Q 是 FF2 的从引脚 CLK 到引脚 Q 的延时,Tsetup 是 FF3 的建立时间,

这两个参数都由工艺库提供。总结完成之后,下面对时钟进行建模,也就是

寄存器到寄存器之间的路径进行约束,时钟的建模是比较复杂的,因此先一

步一步地讲解,最后给出约束脚本。

定义时钟的命令为:create_clock。假设时钟周期为 10ns,定义时钟的命令

就是:

create_clock -period 10 [get_ports clk]

对于的时钟波形为:

定义时钟时(虚拟时钟除外,虚拟时钟在后面说),我们必须定义时钟周期(也

就是-period 这个选项)和时钟源(端口或引脚)(也就是设计中的 clk),也可

以加上一些可选项(option)来定义时钟的占空因数(duty cycle),偏移
(offset/skew)和时钟名( clock name),我们可以通过 man create_clock 来

查看命令的相关选项。

一旦定义了时钟,对于寄存器之间的路径,我们已经做了约束。我们可以用

report_clock 命令来查看所定义的时钟以及其属性。如果我们需要使用时钟

的两个沿(上升沿和下降沿),时钟的占空因数将影响时序的约束。

然而单单定义一个时钟周期进行约束寄存器与寄存器之间的路径很显然是

过于理想的,需要再添加其他的时钟属性,在添加之前,需要知道时钟的偏

移(skew)、抖动(jitter)、转换时间(transition)、延时(latency)

这几个概念或者这几个时钟的属性,这些属性请查看我的另一篇博文:

http://www.cnblogs.com/IClearner/p/6440488.html

该博文详细介绍了时钟的建模,也就是路径 2 的约束。

③路径 1(输入端口到寄存器 D 端)的约束:

这里讨论的是模块前后使用的是同一个时钟 CLK,如下图所示,至于使用不

同的时钟(比如前面的模块是 ClkA 而不是 Clk,那么约束就不一样了)放

在后面说。
在上图中,在 Clk 时钟上升沿,通过外部电路的寄存器 FF1 发送数据经过输

人端口 A 传输到要综合的电路,在下一个时钟的上升沿被内部寄存器 FF2

接收。它们之间的时序关系如下图所示:

对于我们要综合的模块,DC 综合输入的组合逻辑,也就是上面的电路 N,

得到它的延时是 Tn,但是这个 Tn 是否满足的要求(比如说满足触发器的建立

时间)呢?在进行约束之前,DC 是不知道的,因此我们通过约束这条路径,

也就是告诉 DC 外部的延时(包括寄存器翻转延时和组合逻辑、线网的传输

延时)是多少,比如说是 Tclk-q+Tm,在约束了时钟之后,DC 就会计算这

条路径留给电路 N 的延时是多少,也就是 Tclk-q+Tm。然后 DC 就拿 Tclk

-(Tclk-q+Tm)和 Tn+Tsetup 相比较,看 Tclk -(Tclk-q+Tm)是否比

Tn+Tsetup 大,也就是看综合得到的电路 N 的延时 Tn 是不是过大,如果

Tn 太大,大于 Tclk -(Tclk-q+Tm),那么 DC 就会进行优化,以减少延时。

如果延时还是太大,DC 就会报错。因此我们要进行输入端口的约束,告诉

外部电路的延时是多少,以便 DC 约束输入的组合逻辑。
如果我们已知输入端口的外部电路的延迟(假设为 4 ns,包括翻转延时和外

部的逻辑延时),就可以很容易地计算出留给综合电路输入端到寄存器 N 的

最大允许延迟:

DC 中,用 get_input_delay 命令约束输人路径的延迟:

set_input_delay -max 4 -clock CLK [get_ports A]

我们指定外部逻辑用了多少时间,DC 计算还有多少时间留给内部逻辑。在

这条命令中,外部逻辑用了 4 ns,对于时钟周期为 10 ns 的电路,内部逻辑

的最大延迟为 10 - 4 - Tsetup = 6 。

例如,对于下面的电路:

输入端口延时的约束如下所示:

create_clock -period 20 [get-ports Clk]

set_input_delay -max 7.4 -clock Clk [get-ports A]

对应的时序关系图如下所示:
如果触发器 U1 的建立时间为 1ns,则 N 逻辑允许的最大延迟为:

20 - 7 .4 - 1 = 11 .6 ns

换言之:如果 N 逻辑允许的最大逻辑为 11.6ns,那么可以得到外部输入最大

的延时就是 20-11.6-1=7.4ns.

上面是没有考虑不确定因素情况,当考虑不确定因素时,则有:

当有抖动和偏移的时候(假设不确定时间为 U),如果触发器 U1 的建立时

间为 1ns,外部输入延时为 D(包括前级寄存器翻转和组合逻辑的延时),

则 N 逻辑允许的最大延迟 S 为:

20-D-U-1=S,同样可以得到外部输入的延时为:20-U-1-S=D

当输入的组合逻辑有多个输入端口时,如下图所示:
则可以用下面命令对除时钟以外的所有输人端口设置约束:

set_input_delay 3.5 -clock Clk -max [remove_from_collection

[all_ inputs ] [get_ports Clk] ]

remove_from_collection [all_inputs] [get_ports Clk]”;#命令表示从所有

的输入端口中除掉时钟 Clk。

如果要移掉多个时钟,用下面的命令:

Remove_from_collection [all_inputs] [get_ports "Clk1 Clk2"]

3、实战

首先设计的模块如下所示:

设计(约束)规格书如下所示:

(时钟的定义)
(寄存器建立时间定义)

(输入输出端口的延时定义)

(组合逻辑的定义)

上面的规格定义用来给我们进行时序约束使用,现在实践开始。

 创建.synopsys_dc.setup 文件,设置好 DC 的启动环境

-->common_setup.tcl 文件:
由于这里有物理库,因此可以使用 DC 的拓扑模式进行启动。

-->dc_setup.tcl 文件:

-->.synopsys_dc.setup 文件:

 启动 DC,查看 target_library 的信息

-->启动的时候,我们使用管道开关,把 DC 的启动信息保存到

start_report.log 里面(dc_shell -topo 是 DC 的启动命令,启动时产生的


信息,通过 | tee -i 流入 start_report.log 文件中):

(我们也可以通过启动 gui 界面进行输入命令,也可以在 shell 中输入命令)

-->由于我们仅仅是需要查看 target_library 库的信息,因此我们只需要读

入库:read_db sc_max.db

-->然后我们查看与这个库相关联的工艺库:list_libs,结果为:

我们可以看到,sc_max.db 是 target_library 的的文件名称,而

target_library 的库名字是 cb13fs120_tsmc_max

-->接着我们查看库信息:

这里我们使用重定向的命令,将报告的结果保存到哦 lib.rpt 这个文件中。

redirect 是重定向的命令,-file 是将命令产生信息保存到文件中,lib.rpt 是

要保存信息到文件,后面的{}中存放的是要执行的命令。

然后在终端读取相应库的单位信息,时序单位为 ns,电容单位为 pf

 创建约束

在完成启动文件的书写之后,我就需要根据设计规格书,进行书写约束了

-->时钟的约束(寄存器和寄存器之间的路径约束):
1.时钟频率为 333.33MHz,因此时钟周期就是 3ns:

create_clock -period 3.0 [get_ports clk]

2.时钟源到时钟端口的(最大)延时即 source latency 是 0.7ns

set_clock_latency -source -max 0.7 [get_clocks clk]

3.时钟端口到寄存器的时钟端口延时即 network latency 为 0.3ns:

set_clock_latency -max 0.3 [get_clocks clk]

4.时钟周期有 0.04ns 的抖动

5.需要为时钟周期留 0.05ns 的建立时间余量

这里我们就要设置不确定因素了,由于设计规格声明是对建立时间

留余量,因此我们主要考虑建立时间的不确定因素:

首先是时钟偏移为±30ps,则有可能是前级时钟往后移 30ps,同时本

级时钟往前移 30ps,对于建立时间偏移的不确定因素为 30+30

=60ps;

然后是时钟抖动,前级的时钟抖动影响不到本级,因此只需要考虑

本级的时钟抖动,由于是考虑建立时间,因此考虑本级时钟往前抖

40ps,即对于建立时间抖动的不确定因素为 40ps;

最后是要留 50ps 的建立时间不确定余量;

因此对于建立时间,总的不确定时间为 60+40+50=150ps=0.15ns:

set_clock_uncertainty -setup 0.15 [get_clocks clk]

6.时钟转换时间为 0.12ns:

set_clock_transition 0.12 [get_clocks clk]

-->输入延迟约束(输入路径的约束):
1.规定模块内 data1 和 data2 端口的逻辑 S 延时最大为 2.2ns,并没有

直接告诉外部逻辑的延时,因此我们需要计算:

外部最大的延时为:clock period - clock uncertainty - delay of S -

register setup time = 3.0 - 0.15 - 2.2 - 0.2 = 0.45ns,

因此有:

set_input_delay -max 0.45 -clock clk [get_ports data*]

2.对于 sel 端口,由于明显地、直接说了从外部数据发送端(指的是 F3

的 clk)到 sel 端口的 latest(最大)绝对延时是 1.4ns,也就是说,这

个绝对延时包括了时钟的 latency 延时,而 input_delay 是不包括的,

input_delay 是相对时钟的前级逻辑延时,是不包括时钟的 latency,那

么就需要减去时钟的 latency(包括 source 和 network):

1.4ns-(700ps + 300ps) = 0.4ns,那么就有:

set_input_delay -max 0.4 -clock clk [get_ports sel]

-->输出延时约束(输出路径的约束):

1.直接告诉了在 out1 的外部组合逻辑的最大延时为 0.42ns,后级触发

器的建立时间为 0.08ns,也就是外部延时为 0.42+0.08=0.5ns:

set_output_delay -max 0.5 -clock clk [get_ports out1]

2.内部延时为 810ns,应用前面的公式:时钟周期-内部延时(翻转与内部

组合逻辑延时)-不确定时间=外部延时(外部组合逻辑+后级寄存器的

建立时间),于是有:3-0.81-0.15=2.04ns,于是有:

set_output_delay -max 2.04 -clock clk [get_ports out2]

3.意思是外部延时只有后级寄存器的建立时间要求:
set_output_delay -max 0.4 -clock clk [get_ports out3]

-->组合逻辑的约束:

根据前面的公式可以得到:

3-0.15-输入延时-2.45=输出延时,于是可以得到:

输入延时+输出延时 = 0.4ns

由于设计规格没有规定这个比例,因此只要满足输入输出延时的关系满足上

面的式子都可以,如果综合后有违规,我们后面可以再适当调整一下,设置

为:

set_input_delay -max 0.3 -clock clk [get_ports Cin*]

set_output_delay -max 0.1 -clock clk [get_ports Cout]

-->检查语法:

 启动 DC

读入设计前的检查

 读入设计(和查看设计)

这里和流程一样。主要是 read、current_design 、link、check_design,

这里就不具体演示了。

 应用约束和查看约束

-->直接执行 source scripts/MY_DESIGN.con 进行应用约束


-->查看有没有缺失或者冲突的关键约束:

check_timing,返回值为 1,表示执行成功。

-->验证时钟是否约束正确:

report_clock

report_clock -skew

report_port -verbose

-->保存约束好的设计:

write -format ddc -hier -out unmapped/MY_DESIGN.ddc

 综合

(简单的步骤跟流程一样)

 综合后检查(与优化)

(简单的步骤跟流程一样)

 保存综合后的设计

(简单的步骤跟流程一样)
④路径 3(寄存器到输出端口)的约束:

在了解了路径 1 的约束直接之后,路径 3 的约束就变得容易理解了,路径 3

与外部输出电路的的电路图如下所示:

clk 时钟上升沿通过内部电路的寄存器 FF2 发送数据经要综合的电路 S,到

达输出端口 B,在下一个时钟的上升沿被到达外部寄存器的 FF2 接收。他们

之间的时序关系如下图所示,我们要要约束的的组合路径电路 S 的延时,要

DC 计算它的延时是否能够满足时序关系,就要告诉 DC 外部输出的延时大

概是多少:
当我们已知外部电路的延迟(假设为 5.4ns),就可以很容易地计算出留给要

综合电路输出端口的最大延迟,如下图所示:

DC 中,用 set_output_delay 命令约束输出路径的延迟,对于上面的电路图,

有:

set_output_delay -max 5.4 -clock Clk [get_ports B]

我们指定外部逻辑用了多少时间,DC 将会计算还有多少时间留给内部逻辑。

举个例子说,对于下面的这个电路模型:
寄存器到输出端口的时序路径约束为:

create_clock -period 20 [get_ports Clk]

set_output_delay -max 7.0 -clock Clk [get_ports B]

对应的时序关系图如下所示:

如果 U3 的 Tclk-q = 1. 0ns,则 S 逻辑允许的最大延迟为:

20 - 7 .0 - 1=12 ns,也就是说如果 S 逻辑到最终的延时大于 12ns,那么这条

时序路径就会违规,DC 就会报错。

上面是没有考虑抖动和偏移的,内部延时为 S(包括 clk-q 和组合逻辑延时),

外部输出延时为 X(包括外部组合逻辑和后级寄存器的建立时间),时钟周

期为 T,那么就有:

T-S=X,知道了最大的内部延时 S,就可以算出输出外部允许的最大延时 X

当考虑抖动偏移等组成的 uncertainty 因素时,假设不确定时间为 Y,那么

就有:

T-Y-S=X,因此外部输出延时 X,可以直接得到,也可以通过内部延时间 S

(和不确定时间 Y)接计算得出 X。

在这里说一下关于输入路径延时和输出路径延时的一些实际情况。 进行

SOC 设计时,由于电路比较大,需要对设计进行划分,在一个设计团队中,
每个设计者负责一个或几个模块。设计者往往并不知道每个模块的外部输入

延迟和/或外部输出的建立要求(这些要求或许在设计规格书里面写有,或

许没有,当没有的时候设计者就不知道了),如下图所示:

这时,我们可以通过建立时间预算(Time Budget),为输入/输出端口设置时

序的约束,也就是先预置这些延时,大家先商量好(或者设计规格书声明好)。

但是预置多少才合适呢?就有下面的基本原则了:

DC 要求我们对所有的时间路径作约束,而不应该在综合时还留有未加约束

的路径。我们可以假设输人和输出的内部电路仅仅用了时钟周期的 40%。如

果设计中所有的模块都按这种假定设置对输人/输出进行约束,将还有 20%

时钟周期的时间作为富余量( Margin),富余量中包括寄存器 FF1 的延迟和

FF2 的建立时间,即:富余量=20%时钟周期 - Tclk-q - Tsetup,如下图所

示:

举个例子说,对于前面的电路,就要按照这么一个比例进行设置:
对应的约束为:

create_clock -period 10 [get-ports CLK]

set_input_delay -max 6 -clock CLK [all_inputs]

remove_input_delay [get ports CLK] ;#时钟不需要输入延迟的约束

set_output_delay -max 6 -clock CLK [all-outputs]

如果设计中的模块以寄存器的输出进行划分,时间预算将变得较简单,如下

图所示:

时间预算的约束为:

create_clock -period 10 [get-ports CLK]

set_input_delay -max $Tclk-q -clock CLK [all_inputs]

remove_input_delay [get ports CLK] ;#时钟不需要输入延迟的约束

set_output_delay -max [expr 10-$Tclk-q] -clock CLK [all-outputs]

⑤路径 4 的约束
路径 4 是组合逻辑的路径,组合逻辑的约束可能需要虚拟时钟的概念。组合

逻辑可能有两种中情况,一种是前面电路中的路径 4
模块里面有输入端口到输出端口的组合逻辑外,也有时序逻辑,也就是模块

里面有时钟,那么就可以对于路径 4,就下面的电路模型进行约束

组合逻辑部分 F 的延时 Tf 就等于时钟周期 T-Tinput_delay-Toutput_delay,

时钟周期减去两端,就得到了中间的延时约束了,对于上面的模型,可以这

样约束为:

set_input_delay 0.4 -clock CLK -add_delay [get_ports B]

set_output_delay 0.2 -clock CLK -add_delay [get_ports D]

set_max_delay $CLK_PERIOD -from [get_ports

B] -to [get_ports D]

当然,最后一句的约束可有可无。对于多时钟的同步约束,只需要修改相应

的延时和时钟就可以了,可以参考前面的多时钟同步时序约束那里。

当考虑有不确定因素时,假设 F 的延时是 F,外部输入延时为 E(clk-q+组

合逻辑延时),外部输出延时为 G(组合逻辑延时+后级寄存器建立时间),

不确定时间为 U,时间周期为 T,则有(最大频率下):


T-F-E-U = G

另外一种是纯的组合逻辑,模块内部没有时钟:

这种时钟需要用到虚拟时钟的概念,后面介绍有虚拟时钟的约束时,再进行

说明。
七、环境、设计规则和面积约束

本文的主要内容是讲解(约束针对的是逻辑综合下的约束,而实战部分则是在 DC

的拓扑模式下进行):

 环境属性的约束

 设计规则的约束

 面积的约束

 实战(部分)环境属性的约束

1、工作环境属性约束

输入/输出端口及其驱动属性是设计规格的一部分,工作环境的约束,是对

这个规格约束的一部分。

工作环境约束一方面是设置 DC 的工作环境,也就是 DC 要从在什么样的环

境下对你的设计进行约束,举个例子,比如你的芯片要在恶劣的环境中进行

工作,DC 如果在优质的环境中对你的设计进行约束,你的芯片生产出来,

就很有可能工作不了。因此一般就要告诉 DC,使用恶劣的模型对设计进行

约束。另一方面是为了保证电路的每一条时序路径延时计算的精确性,特别

是输入/输出路径的精确性,单单靠外部的输入延时和输出延时的约束是不

够,还要提供设计的环境属性。

这个约束可以看成是宏观的、整体的、外在的约束,约束的内容可以从下图

看出:
 设置环境条件(set_operating_conditions):

用于描述制造工艺、工作电压与温度(PVT,process,voltage,temperature)

这些周围环境对延时的影响。工艺库单元通常用“nominal”电压和温度来

描述其默认的特性,例如:

器件与线网上的延时在条件不同的时候呈线性变换。库文件中,包含对各种

不同条件的具体描述,如 slow,fast,typical 等,对于芯片工作的最差、最好

及典型的具体描述(具体见标准单元库那一节)。通过设置不同的操作条件,

可以覆盖到各种不同的情况。

如果电路在不同于“nominal”电压和或温度的条件下工作,我们需要为设

计设置工作条件(Operating Conditions),我们可以用

set_operating_conditions 命令把工作条件加入到设计上。综合时,原来按

"nominal”环境计算出的单元延迟和连线延迟,将按工作条件作适当的比例
调整,如下图为为延迟与工作条件的关系:

讲解完工作环境条件是什么之后,下面就介绍与它有关的常见的命令以及约

束。工艺库中通常指定一个默认的工作条件.我们可用:report_lib libname

命令把厂商提供的所有工作条件列出来。

设置工作条件可用下面命令举例如下:

set_operating_conditions -max $OPERA_CONDITION -max_library

$LIB_NAME

 设置线负载模型(set_wire_load_model)

在计算时序路径延迟时,除了需要知道门单元延迟外,还需要知道连线的延

迟,如下图所示:

门单元的延迟一般用非线性延迟模型(non-linear delay model)算出。半导


体厂商提供的工艺库中,有两个二维表格,根据门单元的输入转换时间和输

出负载在表中找出门单元的延迟以及其输出转换时间。输出转换时间又作为

下一级电路的输人转换时间。门单元的延迟在综合库那一节详细介绍。

连线的延迟目前一般用(连)线负载模型( Wire Load Model,简称 WLM)

估算。WLM 是厂商根据多种已经生产出来芯片的统计结果,在同样的工艺

下,计算出在某个设计规模范围内(例如门数为 0~43478.00、门数为

43478.00~86956.00,等等)负载扇出为 1 的连线的平均长度,负载扇出为 2

的连线的平均长度,负载扇出为 3 的连线的平均长度等等。WLM 是根据连

线的扇出进行估算连线的 RC 寄生参数,一般情况下,由半导体厂商建立。

厂商根据已生产出来的其他设计统计出该工艺的连线寄生参数。半导体厂商

提供的工艺库中包括了线负载模型。通常在一个综合库里面有多种线负载模

型,不同的模型模拟不同规模的的模块内的线上负载情况(具体见综合库描

述那一节)。用户也可以自己创建自己的线负载模型去更精确地模拟设计内

的线上负载。连线负载模型的格式如下图所示(上),具体格式由工艺库决

定(下):
关于线负载模型的更多内容,可以参考综合库/工艺库那一节。

线负载模型为 DC 提供统计性估算的线网负载信息,随后 DC 使用这些线网

负载信息,以负载的大小为函数来模拟线上的延时(也就是设置这个约束是

用来模拟线延时)。因此设置线负载模型。

如 DC 遇到连线的扇出大于模型中列出的最大扇出值,它将使用外推斜率

(Extrapolation slope)来计算连线的长度。上例格式中,如果连线的扇出为

7,而连线负载模型中最大扇出是 5,连线的其长度计算如下:

讲完线负载模型是什么东西,为什么设置线负载模型约束之后,下面介绍与

其相关的命令。

如果要查看工艺库中的 WLM,可以使用命令:report_lib $lib_name,进

行综合时,综合工具会默认根据设计面积和节点处的负载自动选择合适的连

线负载模型,如果要关掉自动选择 WLM,那么可以使用命令:

set auto_wire_load_selection false

然后手动选择线负载模型的命令是:
set_wire_load_model -name $WIRE_LOAD_MODEL -library $LIB_

NAME

如果连线穿越层次边界,连接两个不同的模块,那么有三种方式对这种跨模

块线连接的类型进行建模,set_wire_load_mode 命令用于设置连线负载模

型的模式。有三种模式供选择:top、segment 和 enclosed。三种模式的

示意图如下所示:

由图可见, 该设计的顶层设计(top)包括一个子模块, 该子模块又包括

两个更小的模块。 顶层设计对应的连线负载模型为 50×50; 子模块对应的

连线负载模型为 40×40;更小的两个模块对应的负载模型为 20×20 和 30×

30。
假设有一个连线贯穿两个小模块, 但没有超出子模块的范围。 对于这种连

线,在三种模式下,所用的连线负载模型是不同的,下面是介绍:

比较悲观的形式:这时,top 为顶层设计,电路的规模比 SUB 模块大,连线

负载模型最悲观。在 top 模式下, 采用 top 层的连线负载模型, 即 50×

50;因此,连线的延迟最大。我们一般选用这种方式,命令如下:

set_wire_load_mode top

比较不悲观方式:用 enclosed 的方式选择 WLM,该 W LM 对应的设计完全

地包住这条连线,这时 DC 将选择 SUB 模块对应的连线负载模型。在

enclosed 模式下, 采用子模块的连线负载模型, 即 40×40;因 SUB 模块

比较 TOP 设计小,所以连线的延迟比较短(不悲观,就是连线延时小)。

对应的命令为:

set_wire_load_mode enclosed

在 segmented 模式下, 位于两个小模块中的部分采用这两个小模块对应

的连线负载模型, 中间部分采用子模块的连线负载模型。

驱动强度、电容负载这些约束是要经验的,一方面是对 I/O 口进行约束,属

于 I/O 口的约束,为时序约束与时序分析提供了路径,更是为输入/输出路

径延时约束的精确性提供保证;一方面是对 I/O 口对外的环境进行约束,可

以算是属于环境约束,因此放在这里进行讲解。

 设置驱动强度(set_drive 与 set_driving_cell)

为 input 或者 inout 的端口设定驱动。Set_drive 以电阻值为计量,0 表示

最大驱动强度,通常为时钟端口;而 set_driving_cell 则以模拟端口驱动的

器件形式计量,说明输入端口是由一个真实的外部单元驱动。
为什么要需要为 input 或者 inout 的端口设定驱动?这是因为,对于输入端,

为了精确计算输入电路的延时时间,DC 需要知道到达输入端口的转换时间:

在默认情况下,DC 假设输入端口上的外部信号转换时间是 0,也就是从上

升沿跳变到下降沿或者下降沿跳变到上升沿的时间是 0,这是不符合实际情

况的。通过设置驱动强度,就告诉 DC 这个设输入端口实际上是由一个真实

的外部单元驱动的,不是理想的;DC 就会计算输入信号的实际转换时间,

仿佛指定某一个库单元正在驱动输入端,下面是输入端 IN1 由 FD1 的输出

引脚驱动的例子:

对应的约束可以这样写:

set_driving_cell -lib_cell FD1 -pin Q [get_ports IN1]

如果不用开关选项“-pin",DC 将使用所找到的第一只引脚(库中这个单元

的第一个引脚,这个驱动引脚貌似是要选择输出的引脚)。

此外,除了通过这两种方式,还可以直接设置输入端口的转换时间,如下所

示:
set_input_transition 时间 [get_ports 设计对象]

 设置电容负载(set_load 与 set_load load of)

明确说明端口(输入或输出)上的外部电容负载。对于输出端,为了精确地

计算输出电路的延时时间,DC 需要知道输出单元所驱动的总负载

默认情况下,DC 假设端口上的外部电容负载为 0。我们可以指定电容负载

为某些常数值,也可以通过用 load_ of 选项明确说明电容负载的值为工艺库

中某一单元引脚的负载(一般是选择输入引脚):

跟设计输入输出延时一样,设计者往往并不知道每个模块输入端口的外部驱

动单元和/或输出端口的外部输出负载。因此我们要通过负载预算(Load

Budget),为输入/输出端口设置环境的约束。产生负载的原则如下:
1.保守起见,假设输入端口为驱动能力弱的单元驱动(即转换时间长);

2.限制每一个输入端口的输入电容(负载);

3.估算输出端口的驱动模块数目。

例如:(只是举例)对于下面的电路图:

其规格为:模块输入端口驱动的负载不大于 10 个“AND2”门的输入引脚

的负载,模块输出端口最多允许连接 3 模块,如果某个输出端需要连接多于

3 个模块,我们要在代码中复制该输端口。

对应的约束

环境约束举例如下
语法中,设置(定义变量时),如上面的:

Set ALL_IN_EXCEPT_CLK [remove_from_collection

[all_inputs] [get_ports “$CLK_NAME”]]中,后面的

remove_from_collection 是移除设计物集对象的意思,我们知道,DC 可以

将设计识别成多个对象,比如输入端口、输出端口等等,然后这个就是从

all_inputs 这个对象集合中,移除掉 CLK_NAME 这些代表的端口;如果命

令之中有命令时,需要用[]来括起来。

然后是 expr 是表达式求值的意思,因为 load_of 取出了一个端口的负载值

(因为哟 load_of 这个命令,即命令中有命令,因此需要加[]),这个值*10

是个表达式,因此用 expr 来指出求值,求值是一个命令,因此用[]括了起来。

上面中,LIB_NAME、WIRE_LOAD_MODEL、DRIVE_CELL 、DRIVE_PIN、

OPERA_CONDITION 这些变量的内容都不是随便定义的,需要根据综合库

书写,下面进行解释,库的具体内容参考综合库那一节。

LIB_NAME:库的名字,这里使用的恶劣的情况:
WIRE_LOAD_MODEL:线负载模型,打开 slow.lib 这文件,可以找到各种

线负载模型:

约束中选的是 w150 的。

DRIVE_CELL:驱动单元,也就是用来模拟驱动输入端的驱动单元,这要选

择库中有的单元,比如反相器:

DRIVE_PIN:驱动管脚,为单元的输出管脚,也就是“Y”了。

OPERA_CONDITION:这个操作环境也是要填写库里面有的:
设置完这些变量之后,还设置了 ALL_IN_EXCEPT_CLK 和 MAX_LOAD 变量,

其中 ALL_IN_EXCEPT_CLK 变量代表了除了时钟之外的输入管脚。

MAX_LOAD 变量就表示最大的负载,代表的是库中某个单元的输入负载值。

最后面的就是设置工作环境了。

2、设置设计规则约束

set_max_transition、set_max_fanout、set_max_capacitance

主要是设置最大转换时间、最大扇出及最大负载电容要求,可以设置在输入

端口、输出端口以及当前设计。举个前面的环境约束的例子说,比如我约束

了输入端口的最大转换时间和负载,也约束输出最大扇出,如下图所示

当综合和优化了,计算发现输入端口实际的转换时间比约束的大,或者负载

比约束的大(模块输入端口驱动的负载大于 10 个“AND2”门的输入引脚

的负载),或者检测到输出端口的扇出数比约束的要多(模块输出端口最多

允许连接 3 模块,如果某个输出端需要连接多于 3 个模块),这时候就违背


了设计规则,DC 就会报错了。

半导体厂商在工艺库强加了设计规则。这些规则根据电容、转换时间和扇出

(capacitance,transition 和 fanout)来约束有多少个单元可以相互联结。设

计规则一般由半导体厂商提供,在使用工艺库中的逻辑单元时对其联结所强

加的限制。例如,如果设计中一个逻辑单元的负载(其驱动的负载)大于库中

给定的其最大负载电容(max_capacitance)值,半导体厂商将不能保证该电

路能正常工作。我们只可以按照设计规则的约束或按照更严格的设计规则约

束来设计电路,而不可以放松约束。Design Compiler 在综合时使用加入缓

冲器(buffering)和改变门单元的驱动能力(cell sizing)技术来满足设计规则

的目标。

库单元的设计规则(design rule)一如下所示:

在约束工作环境的时候,调用了库中的一些单元(的引脚),就相当间接设

置了设计规则约束,我们可以不必设置。
下面我们来详细叙述一下 DC 进行综合时设计规则约束:

DC 在做综合时,把设计规则的优先级设置为最高。优先级的从高到低次序

为:最大电容(max_capacitance)、最大转换时间(max_transition)、最大扇

出(max_fanout).

 最大电容(max_capacitance)的约束

例如对于下面的电路:

对应的最大的电容约束如下:

【1】set DRIVE_PIN TECH_LIB/invla27/Y

【2】set MAX_CAP [get_attribute $DRIVE_PIN max_capacitance]

【3】set CONSERVATIVE_MAX_CAP [expr $MAX_CAP/2.0]

【4】set max_ capacitance $CONSERVATIVE_MAX_CAP [get_ports

IN1]

约束的意思是:首先【1】处,我们选择使用综合库里面库单元的一个输出

引脚作为设计中预期驱动器的最大允许电容负载,其值通过【2】得到,假

设为 3.5pf,也就是 MAX_CAP = 3.6pf。然后通过【3】设置约束时使用的

最大电容变量,除以 2 是在驱动器增加一些富余量使 DC 不会给它加满载。

最后【4】就是设置输入引脚 IN1 的最大电容量为 CONSERVATIVE_

MAX_CAP,也就是 1.8pf。

当设置了输入端口的负载之后,也就是:
set_load 1.2 [ get_ ports IN1]

那么 DC 可以给输入端口 IN1 施加的最大内部负载是:1.8 - 1.2 = .06pf。

我们可以用“set_max_capacitance 3. 0 $ current_design”命令为整个

设计中加入最大电容的设计规则。此处,用了 3. 0 为最大的电容值,设计时

我们可以根据工艺库和电路的具体情况,选用合适的数值。要注意不要施加

过度保守的约束,以免严重地限制 DC 对设计的优化。当然,如果库内定的

值不够恰当或者过于乐观,我们可以手动设置,以控制设计的裕量。

 最大转换时间(max_transition)的设计约束

例如前面的电路,可以进行下面的最大转换时间约束:

#从工艺库找出设计中预期驱动器的最大允许转换时间,也就是最大转换时

间可以像这个引脚这么大,假设其值为 0.400ns。

set DRIVE_PIN TECH_ LIB/invla27/Y

set MAX_TRANS [get_attribute $DRIVE_PIN max_transition]

#在实际驱动的时候,增加一些富余量使 DC 不会给它加满载,也就是实际

上的转换时间不超过 CONSERVATIVE_ MAX_TRANS,也就是 0.200ns。

set CONSERVATIVE_MAX_TRANS [expr $MAX_TRANS / 2.0}]

set_ max_transition $CONSERVATIVE_MAX_

TRANS [get_ports IN1]

约束之后,DC 考虑驱动单元的类型和它的外部负载,DC 限制输入端口 IN1

的内部负载来满足的设计规则,也就满足你这个转换时间,让它不超过该

0.2ns。

有时候我们可以在整个设计上施加最大转换时间的设计约束,以帮助防止可
能在长连线上出现的慢(长)的转换时间,从而导致设计中出现特别长的延迟。

施加最大转换时间的设计约束也可以约束单元输出端的转换时间以减少其

功耗。我们可以用’set_max_transition 0. 4 $ current_design,命令在

整个设计中加入最大转换时间的设计规则。此处,用了 0. 4 为最大转换时间

值,设计时我们可以根据工艺库和电路的具体情况,选用合适的数值。要注

意不要人为地加紧对整个设计的约束,以免限制 DC 对设计中真正关键器件

的适当优化。

 最大扇出(max_fanout)的设计规则的约束

例如对于下面的电路:

用 set_max_fanout 命令为设计设置最大扇出的设计规则的约束,例如:

set_max_fanout 6 [get_ports IN1]

要注意 set_max_fanout 命令使用的是扇出负载(fanout_load),而不是绝对

的扇出数目。端口的扇出负载之和必须小于最大扇出的设计规则的约束。进

行上面的约束之后,DC 在综合时会查看有没有违反规则,我们也可以自己

查看有没有违反设计规则,用下面命令可以得到单元 invla 和 invla27 的扇

出负载:

get_attribute TECH_LIB/invla1/A fanout_load --->比如说是 0.25

get_attribute TECH_LIB/invla27/A fanout_load --->比如说是 3.0


因此就有 DC 可以在端口 IN1 连接 6/0.25=24 个 invla1 单元,或在端口 IN1

连接 6/3. 00=2 个 invla27 单元。但是对于上面的电路,其 IN1 端口的扇出

负载之和为:

3*fanout_load(invla1/A)+ 2*fanout_load(invla27/A)=(3 x

0.25)+(2x3.0)=6.75

因此,电路违反了最大扇出负载的设计规则约束。

与扇出有关的还有线负载模型,线负载模型根据连线的扇出数目来估算连线

电阻和电容值。连线的扇出数目定义为单元输出引脚与其他单元的输入引脚

之间连接的数目。与连线的扇出数目不同,扇出负载属性是附加在单元的输

入端口,不同单元可以有不同的扇出负载属性。

我们可以用如下的命令限制整个设计的扇出负载:

set_max_fanout 6 $current_design ,在对 FPGA 做综合,常常使用此命

令。它也适用于 ASIC 的综合,特别是线负载模型和单元的输入负载不大精

确时使用。

一些工艺库中,某些单元的引脚没有扇出负载属性。这时,DC 会检查库中

默认的扇出负载属性(default_fanout_load) 。如果库中没有默认的扇出负

载属性,DC 假设其值为“0",即这些单元的引脚不受扇出负载的设计规则

约束。我们可以强制使端口的扇出数目为 1,即只与一个单元连接,如果库

中单元的扇出负载最小值为 1. 0,用下面的命令加上扇出负载的设计规则约

束:

set_max_fanout 1.0 [all_inputs]

如果库中单元的扇出负载最小值不为 1. 0,我们需要先找出扇出负载最小的
单元(假设为 bufla1),计算出其扇出负载值,然后加上扇出负载的设计规则

约束:

set SMALL_CELL TECH_LIB/bufla1/A

set SMALL_FOL [get_attribute $SMALL_CELL fanout_load]#库

中单元的扇出负载最小值为 0. 5000

set_max_fanout $SMALL_FOL [all_inputs]

扇出负载值是用来表示单元输人引脚相对负载的数目,它并不表示真正的电

容负载,而是个无量纲的数字。

如果我们所用的所有库单元扇出负载为“1",那么 set_max_fanout 1.

0 [all_inputs]约束将强制所有的输人端口扇出数目为 1,即它们只能与一

个单元连接。否则,为了使输入端口只能与一个单元连接,我们要找出库中

哪一个单元的扇出负载最小,在 set_max_ fanout 命令中使用这个值来保证

在这个端口上只连接一个单元。如果单元上没有扇出负载属性并且库中本身

也没有(默认)预设的扇出负载属性,那么把它设为 1. 0 是有意义和效用的。

我们也可以在输出端口上指定扇出负载值。例如,假设一个内部单元驱动几

个其他的单元并且也同时驱动一个输出端口。我们可以用 set_ load 命令来

指定那个输出端口的实际电容负载。set_ load 命令帮助 DC 在综合时遵从

驱动单元的最大电容设计规则,但该命令并没有为驱动单元的扇出提供独立

的约束。在输出端口使用 set_ fanout_ load 命令时,我们可以为输出端口

建立额外的预期扇出负载模型,综合时 DC 同时也会使内部驱动单元的最大

扇出遵守设计规则的要求。

可以使用命令 report_constraint -all_violators 查看是否违反设计规则。


3、面积约束

下面就是进行面积的约束,也就是告诉 DC 综合的电路面积要在多少以内。

在介绍约束命令之前,先了解一下面积的单位。面积的单位可以是:

 2 输入与非门(2-input-NAND-gate)

 晶体管数目(Transistors)

 平方微米(Square microns)

此外,我们往往看到一个芯片是多少多少门,这多少门的数字就是拿芯片的

总面积,除以 2 输入与非门的面积得到的数值。用 report_lib 命令不可显示

面积的单位,我们要询问半导体厂商面积的单位是什么。

如果不设置面积的约束,Design Compiler 将做最小限度的面积优化。设置

了面积的约束后,DC 将在达到面积约束目标时退出的面积优化。如果设置

面积的约束为“0" , DC 将为面积做优化直到再继续优化也不能有大的效果。

这时,DC 将中止优化。注意,对于很大(如百万门电路)的设计,如将面积的

约束设置为“0" , DC 可能要花很长的时间为设计做面积优化。综合时,运

行的时间很长。

在超深亚微米(deep sub-micro)工艺中,一般说来,面积并不是设计的主要

目标,对设计的成本影响不大。因此,我们在初次优化时,可以不设置面积

的约束。优化后,检查得到的设计面积,然后将其乘上一个百分数(例如 8500),

将其结果作为设计的面积约束。再为设计做增量编辑,运行“compile -inc”

命令,为面积做较快的优化。这样做,既可以优化面积,又可以缩短运行时

间。

最后我们用 set_max_area 命令为设计作面积的约束。例如:


set_max_area 10000

当设计不是很大的时候,根据上面的描述,我们就可以使用下面的命令进行

面积约束:

set_max_area 0

让 DC 做最大的面积优化约束。

4、实战

由于种种原因,这里只有环境属性的约束的实战,其他的约束也可以通过上

面的讲解和下面的这个实战进行设计。

首先,我们来看看设计约束规格:

(设计原理图)

(设计规范)

在前面一节的基本时序约束规范上加上下面的规范:
 DC 启动环境的设置我们根据设计原理图和设计规范开始进行实践:

(跟上一节一样,这里不再重复)

 约束文件的编写

基本的时序路径约束和上一节一样,不进行改动,下面进行工作环境的约束:

-->输入端口的驱动设置:

1.要我们使用库里面的 bufbd1 来驱动除了 clk 和 Cin*之外的所有输入端口:

这里是直接使用库里面的单元来驱动的,根据前面的讲解,我们很容易得到

约束的命令为:

set_driving_cell -lib_cell bufbd1 -library cb13fs120_tsmc_max [r

emove_from_collection [all_inputs] [get_ports "clk Cin*"]]

(命令的格式为)set_driving_cell -lib_cell 单元的名字 -library 单元所

在库的名字 要设置约束的对象

注意:当一条命令太长,要进行分割时,使用反斜杠\作为分隔符,且反斜

杠后面不能加空格。

2.Cin*是芯片级的端口,需要加上 120ps 的最大转化时间,这是直接设置转

换时间,因此可以这样约束:

set_input_transition 0.12 [get_ports Cin*]

-->输出负载的约束:
1.除了 cout 输出,其它输出驱动值都是库单元 bufbd7 的引脚 I 负载值的两

倍,也就是用单元的端口进行约束,因此有:

set_load [expr 2 * {[load_of cb13fs120_tsmc_max/bufbd7/I]}]

[get_ports out*]

2.cout 驱动最大值为 25pf 的负载,因此可以这样设置:

set_load 0.025 [get_ports Cout*]

-->操作环境的设置:

由于用到了库里面的单元,我们还在最好设置一下操作环境,虽然 DC 可以

从启动环境里面找到单元所在的位置库,但是我也要设置操作环境,如下所

示:

set_operating_conditions -max cb13fs120_tsmc_max

下面的步骤跟前面的一样,这里就不展开描述了
八、DC 逻辑综合及优化
对进行时序路径、工作环境、设计规则等进行约束完成之后,DC 就可以进行综

合、优化时序了,DC 的优化步骤将在下面进行讲解。然而,当普通模式下不能

进行优化的,就需要我们进行编写脚本来改进 DC 的优化来达到时序要求。理论

部分以逻辑综合为主,不涉及物理库信息。在实战部分,我们将在 DC 的拓扑模

式下进行。(本文主要参考虞希清的《专用集成电路设计实用教程》来写的总结

整理与实验拓展)主要内容有:

 DC 的逻辑综合及优化过程

 时序优化及方法

 实战

1、DC 的综合优化阶段

我们使用 compile 命令就可以让 DC 进行综合优化我们的设计了,这里是使

用普通模式,在拓扑模式下,不支持 compile 命令,而是使用 compile_ultra

命令。电路综合优化包括三个阶段,在这三个阶段,都对设计作优化,如下

图所示:
主要包括:第一阶段的结构级的优化(Architectural-Level Optimization)、

第二阶段的逻辑级优化(Logic-Level Optimization)、最后阶段的门级优化

(Gate-Level Optimization)。

(1) 结构级的优化(Architectural-Level Optimization)

结构级优化包括的内容如下:

①设计结构的选择(Implementation Selection):

在 DesignWare 中选择最合适的结构或算法实现电路的功能。

②数据通路的优化(Data-path Optimization):

选择 CSA 等算法优化数据通路的设计。

③共享共同的子表达式(Sharing Common Subexpressions):

也就是多个表达式/等式中,有共同的表达式,进行共享,举例如下:

有等式:

SUM1<=A+B+C;

SUM2<=A+B+D;

SUM3<= A+B+E;

很容易看出,上面的等式中有共同的表达式 A+B,那么代码子表达

式 A+B 可以被共用,原等式可改为:

Temp=A+B;

SUM1<=Temp+C;

SUM2<= Temp+D;
SUM3<=Temp+E;

这种方法可以把比较器的数目减少,共享共同的子表达式。

④资源共享(Resource Sharing):

对于下面的代码

DC 中经过资源共享之后,就会得到综合出仅用一个加法器和两个多

路传输器的设计,如下图所示,从而节省资源:

算术运算资源共享的默认策略是约束驱动的。我们也可以指示 DC

使用面积优化的策略。即将变量 hlo_resource_allocation 设置为

area,如下所示:

set hlo_resource_allocation area

如果不希望资源共享,可以将即将变量 hlo_resource_allocation 设

置为 none,这时候,我们还是要进行算术运算的资源共享,那么我
们必须在 RTL 代码中写出相应的代码,如下所示

⑤重新排序运算符号(Reordering Operators):

RTL 代码包含电路的拓扑结构。HDL 编译器从左到右解析表示式。

括号的优先级更高。DC 中 DesignWare 以这个次序作为排序的开

始。

例如:表达式 SUM<= A*B+C*D+E+F+G,在 DC 中的综合的结构

如下图所示:

电路的总延迟等于一个乘法器的延迟加上 4 个加法器的延迟。为了

使电路的延迟减少,我们可以改变表达式的次序或用括号强制电路

用不同的拓扑结构
这时得到的综合结构为:

电路的总延迟等于一个乘法器的延迟加上 2 个加法器的延迟,比原

来的电路少了 2 个加法器的延迟。

(2) 逻辑级优化(Logic-Level Optimization)

逻辑优化的内容如下

做完结构的优化后,电路的功能以 GTECH 的器件来表示。在逻辑级优

化的过程中,可以作结构(Structuring)优化和展(开)平(Flattening)优化。

①结构优化:

结构(Structuring)优化用共用子表达式来减少逻辑,这种方式既可

用作速度优化又可用作面积优化。结构优化是 DC 默认的逻辑级优

化策略。结构优化在作逻辑优化时,在电路中加入中间变量和逻辑

结构。DC 作结构优化时,寻找设计中的共用子表达式。例如,对于
下面的电路,优化前为:

做完结构优化后,电路和功能表达式为:

值得一提的是逻辑级的结构优化中共用子表达式和前面结构级的共

用子表达式是不同的,逻辑级的结构优化指门级电路的共用子表达

式,结构级的是算术电路的共用子表达式。逻辑级结构优化并不会

改变设计的层次,用下面的命令设置结构优化:

set_structure true

②展平优化:

展平优化把组合逻辑路径减少为两级,变为乘积之和

(sum-of-products,简称 SOP)的电路,即先与(and)后或(or)的电

路,如下图所示:
这种优化主要用作速度的优化,电路的面积可能会很大。用下面的

命令设置展平优化:

set_flatten true -effort low | medium | high(low 、 medium、

high 其中一个就可以了)

命令选项“-effort”后的默认值为 low,对大部分设计来说,默认

值都能收到好的效果。如果电路不易展平,优化就停止。如果把选

项“-effort”后的值设为 medium, DC 将花更多的 CPU 时间来努

力展平设计。如果把选项“-effort”后的值设为 high,展平的进程

将继续直到完成。这时,可能要花很多时间进行展平优化。

结构(Structuring)优化和展平(Flattening)优化的比较:

(3) 门级优化(Gate-Level Optimization)

门级优化时,Design Compiler 开始映射,完成实现门级电路。主要有

以下内容:
映射的优化过程包括 4 个阶段:

阶段 1:延迟优化

阶段 2:设计规则修整

阶段 3:以时序为代价的设计规则修整

阶段 4:面积优化。

如果我们在设计上加入了面积的约束,Design Compiler 在最后阶段(阶

段 4)将努力地去减少设计的面积。门级优化时需要映射组合功能和时序

功能:

组合功能的映射的过程为:DC 从目标库中选择组合单元组成设计,该

设计能满足时间和面积的要求,如下图所示:
时序功能的映射的过程为:DC 从目标库中选择时序单元组成设计,该

设计能满足时间和面积的要求,为了提高速度和减少面积,DC 会选择比

较复杂的时序单元,如下所示:
设计规则修整的介绍如下:工艺库中包括厂商为每个单元指定的设计规

则。设计规则有:max_capacitance,max_transition 和 max_fanout。映
射过程中,DC 会检查电路是否满足设计规则的约束,如有违反之处.DC

会通过插入缓冲器( buffers)和修改单元的驱动能力(resizes cells)进行

设计规则的修整。修正设计规则的步骤如下所示:

DC 进行进行优化的时候,如果下面的条件之一都满足了:

①所有的约束都满足了;②用户中断;③Design Compiler 到了综合结

果收益递减的阶段,即再综合下去对结果也不能有多大的改善。

这时 DC 就会进行中断优化,停止综合。

(4) 其他的优化情况(需要加上一定的综合选项开关)

比如一个寄存器驱动多个寄存器时,可能会违反设计规则,DC 会把就

驱动寄存器进行复用,同时把被驱动的进行分割
(使用 DC 的拓扑模式,加上 -timing 选项才能自动地使用上面的这种

寄存器复制的优化)

当你的设计中出现多次例化的情况时,也就是下面的情况:

在这种情况下,DC 在编译时,会复制每个例化的模块。每个模块对应

一个拷贝,并且有一个独一无二的名字。这样 DC 可以根据每个模块本

身特有的环境做优化和映射,如下图所示((模块名字唯一化)):

在 DC 里,我们可以用 uniquify 命令为设计中的每一个模块产生一个名

字唯一的拷贝。DC 在为设计做综合(compile)时,也会自动地为每一个

模块产生一个唯一的有名字的拷贝。变量 uniquify_naming_style 可以

用来控制多次例化子模块每个拷贝的命名方式。其详细的使用方法可以

在 DC'中用“man uniquify_naming_style”来查看。
2、时序优化及方法

DC 综合之后,我们查看详细的报告,如果没有违规,设计既能满足时间和

面积的要求又不违犯设计规则,那么综合完成。可以把门级网表和设计约束

等交给后端(backend)工具做布局(placement )、时钟树综合(clock tree

synthesis)和布线(route)等工作,产生 GDSII 文件。如果设计不能满足时间

和面积的要求或违犯设计规则等,就要分析问题所在,判断问题的大小,然

后采取适当的措施解决问题。问题往往是时序的问题,发生时序违规时可以

采取相应的措施,如下图所示:

(1)当违规得比较严重时,也就是时序的违规(timing violation)在时钟

周期的 25%以上时,就需要重新修改 RTL 代码了。

(2)时序违规在 25%以下,有下面的时序优化方法:

①使用 compile_ultra 命令(在拓扑模式下运行)

compile_ultra 跟 compile 一样,是进行编译的命令。compile_ultra

命令适用于时序要求比较严格,高性能的设计。使用该命令可以得
到更好的延迟质量( delay QoR ),特别适用于高性能的算术电路优

化。该命令非常容易使用,它自动设置所有所需的选项和变量。下

面是这个命令的一些介绍:

compile_ultra 命令包含了以时间为中心的优化算法,在编辑过程中

使用的算法有:

 以时间为驱动的高级优化(Timing driven high-level

optimization);

 为算术运算选择适当的宏单元结构;

 从 DesignWare 库中选择最好的数据通路实现电路;

 映射宽扇入(Wide-fanin)门以减少逻辑级数;

 积极进取地使用逻辑复制进行负载隔离;

 在关键路径自动取消层次划分(Auto-ungrouping of

hierarchies)。

compile_ultra 命令支持 DFT 流程,此外 compile_ultra 命令非常

简单易用,它的开关选项有:
部分解释如下所示:

-scan :做可测试(DFT)编辑;

-no_autoungroup :关掉自动取消划分特性;

-no_boundary_optimization :不作边界优化;

-no_uniquify : 加速含多次例化模块的设计的运行时间

-area_high_effort_script : 面积优化

-timinq_high_effort_script : 时序优化

上面的开关部分说明如下所示:

 使用 compile_ultra 命令时,如使用下面变量的设置,所有的

DesignWare 层次自动地被取消:

set compile_ultra_ungroup_dw true (默认值为 true)

也就是说,你调用的一个加法器和一个乘法器,本来他们是以 IP 核

的形式,或者说是以模块的形式进行综合的,但是设置了上面那么

变量之后,综合后那个模块的界面就没有了,你不知道哪些门电路
是加法器的,哪些是乘法器的。

 使用 compile_ultra 命令时,使用下面的变量设置,如果设计中有

一些模块的规模小于或等于变量的值,模块层次被自动取消:

set compile_auto_ungroup_delay_num_cells 100(默认值

=500)

也就是说,假设你有一个模块 A 是一个小的乘法器,并且调用了模

块 B,一个模块 B 是一个小的加法器,使用没有设置这条命令的情

况综合,那么我们可以看到模块 A 中乘法器对应的门电路是哪些,

同样也可以看到模块 B 的加法器是由哪些门电路构成的,模块 A 和

模块 B 之间有层次、有界限;当设置上面的那条命令之后,我们就

看不到模块 A 或者模块 B 之间的层次关系了,也看不到乘法器是由

哪些门电路构成,或者说你看到了某一个与门,但是你并不知道它

是构成乘法器的还是构成加法器的。

为了使设计的结果最优化,我们建议将 compile_ultra 命令和

DesignWare library 一起使用。

 边界优化是指在编辑(又叫综合)时,Design Compiler 会对传输

常数、没有连接的引脚和补码(complement)信息进行优化,如下图

所示:
也就是说,边界优化会把边界引脚一些固定的电平、固定的逻辑进

行优化。

 BRT 技术(-retime option),在 DC Ultra(或者 DC 的拓扑模式

下)中,我们可以用 Behavioral ReTiming(简称 BRT)技术,对门

级网表的时序进行优化,也可以对寄存器的面积进行优化。BRT 通

过对门级网表进行管道传递(pipeline)(或者称之为流水线),使设

计的传输量(throughput)更快。BRT 有两个命令:

optimize_registers :适用于包含寄存器的门级网表(不是

compile_ultra 的开关选项)。

pipeline_design :适用于纯组合电路的门级网表。

对于寄存器的的优化,举例如下,对于下面的电路,既包含有组合

逻辑电路又包含有寄存器:
后级的寄存器与寄存器之间的时序路径延迟为 10. 2 ns,而时钟周

期为 10 ns,因此,这条路径时序违规。但是前级的寄存器与寄存器

之间的时序路径延迟为 7. 5 ns,有时间的冗余。使用

optimize_registers 命令,可以将后级的部分组合逻辑移到前级,

使所有的寄存器与寄存器之间的时序路径延迟都小于时钟周期,满

足寄存器建立时间的要求。optimize_registers 命令首先对时序做

优化,然后对面积作优化。优化后,在模块的入/输出边界,电路的

功能保持不变。该命令只对门级网表作优化。

除了单独使用这个命令之外,还可以在编译的时候往往加上选项

-retime(这个好像只有 compile_ultra 才有这个开关选项)。-retime

选项的功能也就是:当有一个路径不满足,而相邻的路径满足要求

时,DC 会进行路径间的逻辑迁移,以同时满足两条路径的要求,这

也叫 adaptive retiming,如下图所示:
对于纯组合逻辑的流水线(管道)优化,举例如下,对于纯组合逻

辑电路进行优化如下所示:
左边电路,是一个纯组合电路,它的路径延迟为 23. 0 ns。对这个

电路进行管道传递优化后,得到右边所示的电路。显然,电路的传

输量(throughput)加快了。需要注意的是,在使用这个命令时,需

要在 RTL 设计中把寄存器预置好,否则 DC 不知道这些寄存器是怎

么来的。

②使用 compile -scan -inc 命令

-inc 是使用增量编译。这条命令就是进行支持可测性设计的增量编

译。使用增量编辑时,DC 只作门级优化,这时,设计不会回到 GTECH,

如下图所示:

③使用自定义路径组合关键范围

在介绍这种优化方法之前,先来了解一下路径分组与延时。
 路径分组:

DC 为了便于分析电路的时间,时序路径又被分组。路径按照控

制它们终点的时钟进行分组。如果路径不被时钟控制,这些路径

被归类于默认(Default)的路径组。我们可以用

report_path_group 命令来报告当前设计中的路径分组情况。

例如,对于下面的电路,我们来看一下路径及分组情况

根据上图可以知道,图中共有 5 个终点(四个寄存器和一个输出)。
时钟 CLK1 控制 3 个终点,在 CLK1 的控制下有 8 条路径。时钟

CLK2 控制一个终点,在 CLK2 的控制下有 3 条路径。输出端口

为一终点,它不受任何时钟控制,其起点为第二级寄存器的时钟

引脚,在它的控制下只有一条路径,这条路径被归类于默认的路

径组。因此,本设计中共有 12 条路径和 3 个路径组。该 3 个路

径组分别为 CLKI,CLK2 和默认(Default )路径组。

 路径的延时:

在计算路径的延迟时,Design Compiler 把每一条路径分成时

间弧((timing arcs),如下图所示:

DC 就是通过时间弧来计算路径延时。因为时间弧描述单元或/

和连线的时序特性。单元的时间弧由工艺库定义,包括单元的延

迟和时序检查(如寄存器的 setup/hold 检查,clk->q 的延迟等);

连线的时间弧由网表定义。在上面电路中,时间弧有连线的延迟,

单元的延迟和寄存器的 clk -> q 延迟。单元延迟常用非线性模

型计算;连线延迟在版图前用线负载模型计算;RC 寄生参数的分

配用操作条件中的“Tree-type”属性决定;工作条件又决定制程、
电压和温度对连线及单元延迟的影响。

此外,路径的延迟与起点的边沿有关,如下图所示:

假设连线延迟为 0,如果起点为上升沿,则该条路径的延迟等于

1. 5 ns。如果起点为下降沿,则该条路径的延迟等于 2. 0 ns。

由此可见,单元的时间弧是边沿敏感的。Design Compiler 说

明了每一条路径延迟的边沿敏感性。还有需要强调的是 Design

Compiler 默认的行为是假设寄存器之间的最大延迟约束

为:TCLK - FF21ibSetup,即数据从发送边沿到接收边沿的最大

延迟时间要小于一个时钟周期,如下图所示:

Design Compiler 中,常用 report_timing 命令来报告设计的

时序是否满足目标。执行 report_timing 命令时,DC 做 4 个步

骤:
 把设计分解成单独的时间组;

 每条路径计算两次延迟,一次起点为上升沿,另一次起点为

下降沿;

 在每个路径组里找出关键路径(critical path),即延迟最大的

路径;

 显示每个时间组的时间报告。

关于怎么阅读时序报告,我们后面进行介绍。

DC 的默认行为是对关键路径作优化。当它不能为关键路径找到

一个更好的优化解决方案时,综合过程就停止。DC 不会对次关

键路径(Sub-critical paths)作进一步的优化。因此,如果关键路

径不能满足时序的要求,违反时间的约束,次关键路径也不会被

优化,它们仅仅被映射到工艺库,如下图所示:

对于下面的电路,假设加设计约束后,所有的路径属于同样的时

钟组,也就是只有一个路径组:
如果组合电路部分的优化不能满足时序要求,并且关键路径在组

合电路里,根据 DC 的默认行为,组合电路中关键路径的优化将

会阻碍了与它属于相同时钟组的寄存器和寄存器之间路径的优

化。防止出现这种情况可用下面两种方法:自定义路径组和设置

关键范围。

 自定义路径组(User-Defined Path Group):

综合时,工具只对一个路径组的最差(延时最长)的路径作独立的

优化,但并不阻碍另外自定义路径组的路径优化。产生自定义路

径组也可以帮助综合器在做时序分析时采用各自击破

(divide-and-conquer)的策略,因为 report_timing 命令分别

报告每个时序路径组的时序路径。这样可以帮助我们对设计的某

个区域进行孤立,对优化作更多的控制,并分析出问题所在,如

下图所示的:
产生自定义路径组的命令如下所示:

#Avoid getting stuck on one path in the reg-reg group

group_path -name INPUTS -from [all_inputs]

group_path -name OUTPUTS -to [all_outputs]

group_path -name COMBO -from [all_inputs] -to [al

l_outputs]

上面的命令产生三个自定义的路径组,加上原有的路径组,即寄

存器到寄存器的路径组(因为受 CLK 控制,默认的是 CLK 的路

径组),现在有 4 个路径组。组合电路的路径,属于“COMBO”

组,由于该路径组的起点是输入端,在执行

“group_path -name INPUTS -from [all_inputs]”命令

后,命令中用了选项“-from [all_inputs]",它们原先属于

“INPUTS”组。在执行“group_path -name

OUTPUTS -to [all_outputs]”命令后,组合电路的路径不会

被移到“OUTPUTS”组,因为开关选项‘'-from”的优先级高

于选项”-to”,因此组合电路的路径还是留在“INPUTS”路径

组。但是由于
“group_path -name COMBO -from [all_inputs]

-to [all-outputs]”命令中同时使用了开关选项“-from”和

“-to" ,组合电路路径的起点和终点同时满足要求,因此它们最

终归属于“COMBO”组。DC 以这种方式工作来防止由于命令

次序的改变而使结果不同。我们可以用 report_path_group 命

令来得到设计中时序路径组的情况。

产生自定义的路径组后,路径优化如下图所示,此时,寄存器和

寄存器之间的路径可以得到优化:

DC 可以指定权重进行优化,当某些路径的时序比较差的时候,

可以通过指定权重,着重优化该路径。权重最高 5,其次是 2,

默认是 1;因此最差的要设置 5;如下图所示,下面的命令就是

着重优化 CLK 这个路径组


 关键范围(Critical Range):

DC 默认只对一个路径组内的关键路径进行时序优化,但是我们

可以设置 DC 在关键路径的延时下面某个延时值之内的路径进

行优化,因此我们可以使用下面的命令设置关键范围:

set_critical_range 2 [current_design]

使用上面的命令之后,DC 会对在关键路径 2ns 的范围内的所有

路径作优化,解决相关次关键路径的时序问题可能也可以帮助关

键路径的优化。时序优化的示意图如下所示:

如果在执行 set_critical_range 命令后,优化时使关键路径时序

变差,DC 将不改进次关键路径的时序。我们建议关键范围的值

不要超过关键路径总值的 10%。

 自定义路径组+关键范围

这是将自定义路径组合关键范围结合起来,也就是在每一个路径

组用指定的关键范围来设置设计的关键范围,命令如下所示:

group_path -name CLK1 -critical_range 0.3

group_path -name CLK2 -critical_range 0.1

group_path -name INPUTS -from [all_inputs] -critical


_range 0

同时使用自定义时序路径组和关键范围,会使 DC 运行时间加长,

并且需要使用计算机的很多内存。但这种方法值得一试,因为

DC 默认地只在每个路径组优化关键路径。如果在一条路径上关

键路径不能满足时间,它不会尝试其他的方法对该时序路径组的

其他路径做优化。如果能使 DC 对更多的路径做优化,它可能在

对设计的其他部分做更好的优化。在数据通路的设计中,很多时

序路径是相互关联的,对次关键路径的优化可能会改进关键路径

的时序。设置关键范围后.即使 DC 不能减少设计中的最差负数

冗余(Worst NegativeSlack),它也会减少设计中总的负数冗余

(Total Negative Slack) 。

下面是自定义路径组和关键范围的主要区别:

自定义路径组: 用户自定义路径组后,如果设计的总性能有改善,

DC 允许以牺牲一个路径组的路径时序(时序变差)为代价,而使

另一个路径组的路径时序有改善。在设计中加入一个路径组可能

会使时序最差的路径时序变得更差。

关键范围: 关键范围不允许因为改进次关键路径的时序而使同一

个路径组的关键路径时序变得更差。如果设计中有多个路径组,

我们只对其中的一个路径组设置了关键范围,而不是对整个设计

中的所有路径组都设置了关键范围,DC 只会并行地对几条路径

优化,运行时间不会增加很多。

④重新划分模块(Repartition Block)
模块的划分是在设计一开始就进行的,但是由于我们是注重 DC 这个工具的

使用,因此放在这里讲解。

 层次结构与模块划分:

层次结构在 IC 设计中广泛使用。现代的 IC 设计中,几乎没有不用层次结构

进行设计的。一些大的设计,其逻辑层次可能多达十几层。SoC 设计中一般

包括设计的再使用和知识产权 IP 核。SoC 设计中包括了多个层次的电路。

层次化的 IC 设计趋势如下所示:

SoC 设计由一些模块组成,如下图所示:
同样,图中已综合逻辑电路(例如 RISC_CORE),一般也由一些子模块组成。

对于设计复杂规模又大的电路,我们需要对它进行划分(Partitioning),然后

对划分后比较简单规模又小的电路作处理(如综合)。这时,由于电路小,处

理和分析比较方便简单。容易较快地达到要求。再把已处理好的小电路集成

为原来的大电路,如下图所示:

理想情况下,所有的划分应该在写 HDL 代码前已经计划好。

 初始的划分由 HDL 定义好.

 初始的划分可以用 Desige Compiler 进行修改.

做划分的原因很多,下面是其中的几个原因:

 不同的功能块(如 Memory,uP,ADC,Codec、控制器等等);
 设计大小和复杂度(模块处理时间适中,设计大小一般设为一个晚上的运

行时间,白天进行人工处理和调试,晚上机器运行,第二天上午检查运

行结果);

 方便设计的团队管理项目(每个设计工程师负责一个或几个模块);

 设计再使用(设计中使用 IP);

 满足物理约束(如用 FPGA 先做工程样品—Engineering Sample;大的设

计可能需要放入多个 FPGA 芯片才能实现)。

划分模块关系到时序,时序不好的情况下,可以进行重新划分模块,因此就

要求我们在划分模块的时候,对设计进行合适的划分。

可用例化(instantiation)定义设计的层次结构和模块(hierarchical

structure and blocks)。 VHDL 的 entity 和 Verilog 的 module 的陈述

(statements)定义了新的层次模块,即例化一个 entity 或 module 产生一级

新的层次结构。如果设计中,我们用符号(+,一,*,/,…)来标示算术运算

电路,可能会产生一级新的层次结构。VHDL 语言中的 Process 和 Verilog

语言中的 Always 陈述并不能产生一级新的层次。设计时,为了得到最优的

电路,我们需要对整个电路作层次结构的设计,对整个设计进行划分,使每

个模块以及整个电路的综合结果能满足我们的目标。

例如下面的设计中:

有 3 个模块:A,B 和 C。它们各自有输入和输出端口。由于 DC 在对整个电路


做综合时,必须保留每个模块的端口。因此,逻辑综合不能穿越模块边界,

相邻模块的组合逻辑也不能合并。从寄存器 A 到寄存器 C 的路径的延时较

长,这部分的电路面积较大。 如果我们对设计的划分作出修改,相关的组

合电路组合到一个模块,原来模块 A,B 和 C 中的组合电路没有了层次的分隔,

综合工具中对组合电路优化的技术现在能得到充分的使用。这时,电路的面

积比原来要小,从寄存器 A 到寄存器 C 的路径的延时也短了。修改如下:

如果我们对设计的划分作另一种修改,如下所示,我们将得到最好的划分:

这里的修改将相关的组合电路组合到一个模块,原来模块 A,B 和 C 中的组

合电路没有了层次的分隔,综合工具中对组合电路优化的技术能得到充分的

使用。并且,由于组合电路和寄存器的数据输入端相连,综合工具在对时序

电路进行优化时,可以选择一个更复杂的触发器((JK,T,Muxed 和

Clock-enabled 等),把一部分组合电路吸收集成到触发器里。从而使电路

的面积更小,从寄存器 A 到寄存器 C 的路径的延时更短。

对于一般的设计,好的模块划分如下图所示:
在这样的划分下,模块的输出边界是寄存器的输出端。由于组合电路之间没

有边界,其输出连接到寄存器的数据输入端,我们可以充分利用综合工具对

组合电路和时序电路的优化技术,得到最优的结果,同时也简化了设计的约

束。图中每个模块除时钟端口外的所有输入端口延时是相同的,等于寄存器

的时钟引脚 CLK 到输出引脚 Q 的延时。这使得时序约束更为方便,这一点

在前面的时序路径约束中有说过。

上面是推荐的模块划分模式,下面就来说一下哪些是要避免的模块划分方式。

作模块划分时,应尽量避免使用胶合逻辑(Glue Logic),胶合逻辑如下图

胶合逻辑是连接到模块的组合逻辑。图中,顶层的与非门(HAND gate)仅仅是

个例化的单元,由于胶合逻辑不能被其他模块吸收,优化受到了限制。如果采用

由底向上(bottom up)的策略,我们需要在顶层做额外的编译(compile)。避免

使用胶合逻辑(Glue Logic)的划分如下所示:
胶合逻辑可以和其他逻辑一起优化,顶层设计也只是结构化的网表。不需要再做

编译。

 模块划分的修改

第一次的模块划分可能存在时序违规,可能需要重新划分模块,这里就来介绍一

下模块划分的修改问题。我们知道,设计越大,计算机对设计作综合时所需要的

资源越多,运行时间就越长。Design Compiler 软件本身对设计的规模大小并

没有限制。我们在对设计做编译时,需要考虑划分模块规模的大小应与现有的计

算机中央处理器(CPU)和内存资源相匹配。尽量避免下面划分不当情况:

模块太小:由于人工划分的模块边界,使得优化受到限制,综合的结果可能不是

最优的。

模块太大:做编辑所需的运行时间可能会太长,由于要求设计的周期短,我们不

能等太久。

一般来说,根据现有的计算机资源和综合软件的运算速度,按我们所期望的周转

时间(turn around time),把模块划分的规模定为大约 400 ~800K 门。对设计

作综合时,比较合理的运行时间为一个晚上。白天我们对电路进行设计和修改,

写出编译的脚本。下班前,用脚本把设计输人到 DC,对设计作综合优化,第二
天早上回来检查结果。

作划分时,要把核心逻辑(Core Logic) 、 I/0 Pads、时钟产生电路、异步电路

和 JTAG(Joint Test Action Group)电路分开,把它们放到不同的模块里。顶层

设计至少划分为 3 层的层次结构:顶层(Top-level)、中间层(Mid-level)、核心

功能(Functional Core),如下图所示:

使用这种划分方式是因为:I/O pad 单元与工艺相关、分频时钟产生电路是不可

测试(Untestable)的、JTAG 电路与工艺相关、异步电路的设计、约束和综合与

同步电路不同,所以也放在与核心功能不同的模块里。

这里主要介绍同步电路的设计与综合。 为了使电路的综合结果最优化,综合的

运行时间适中,我们需要对设计作合适的划分。如果现有的划分不能满足要求,

我们要对划分进行修改。我们可以修改 RTL 原代码对划分作修改,也可以用 DC

的命令对划分作修改。下面介绍在 DC 里用命令修改划分。

DC 以两种方法修改划分:自动修改划分和手动修改划分。

自动修改划分:

综合过程中 DC 需透明地修改划分。在 DC 中如使用命令:

compile -auto_ungroup area | delay (面积和延时之中选一个)

DC 在综合时将自动取消(去掉)小的模块分区。取消模块分区由变量(前面也有
提及到这些命令):

compile_auto_ungroup_delay_num_cells

compile_auto_ungroup_area_num_cells

来控制。两个变量的预设默认值分别为 500 和 30。我们也可以用 set 命令把它

们设置为我们希望的任何数值。我们可用 report_auto_ungroup 命令来报告编

辑时取消了那些分区。如在 DC 中使用命令:

compile -ungroup_all

DC 在综合时将自动取消所有的模块分区或层次结构。此时,设计将只有顶层一

层的电路。该命令不能取消附加了 dont_touch 属性的模块分区。

手工修改划分:

手动修改划分是指用户用命令指示所有的修改。使用“group”和“ungroup”

命令修改设计里的划分,如下图所示:

group 命令产生新的层次模块,效果如下图所示:
ungroup 命令取消一个或所有的模块分区,效果如下图所示:

如要在当前设计中取消所有的层次结构,可以使用下面的命令:

ungroup -all -flatten

ungroup 命令用选项“-simple_names”将得到原来的非层次的单元名 U2 and

U3

ungroup U23 -simple_names

得到的效果如下图所示:
最后,为了防止再次划分模块,这里总结一下模块划分的策略:

 不要通过层次边界分离组合电路

 把寄存器的输出作为划分的边界

 模块的规模大小适中,运行时间合理

 把核心逻辑(Core Logic) ,Pads、时钟产生电路、异步电路和 JTAG 电路分开

到不同的模块

这样划分好处是:结果更好——设计小又快、简化综合过程——简化约束和脚

本、编译速度更快一一更快周转时间(turnaround)。

下面是实战环节

3、实战

在本次实战里面,我们主要根据给出的原理图和综合规范,实践 DC 的综合优化

技术,在拓扑模式下进行,因此还有可能涉及一些物理设计的内容,我们一步一

步来进行吧。

设计原理图:
(顶层模块示意图:)

(子模块示意图一:)

(子模块示意图二:)
(子模块示意图三:)

综合规范:

(可用资源规范:)

(设计和约束文件说明:)

(布局规划说明:)

(设计规范:)
首先我们来简单分析一下这个综合规范:

可用资源规范:也就是通过运行那个脚本来查看你的电脑有多少可用来综合的核

心,这里我们跳过,不用理他。

设计和约束文件说明:告诉我们设计的 RTL 文件和名字,以及告诉我们约束的

位置和名字,RTL 文件和名字以及时序环境等约束都我们不需要改。

布局规划说明:由于我们使用的是拓扑模式下的综合,这个布局规划提供给了我

们物理的约束信息。

设计规范说明:其实这个是综合的规范说明,告诉你需要在综合过程中,要对哪

些模块进行怎么样的处理,从而达到某种要求,这里面的 10 条规范我们后面在

时间过程中都会介绍。

·设置.synopsys_dc.setup 启动文件,配置 DC 的启动环境(跟前面一样,不进

行具体描述)

·进行编写设计约束文件,由于这里一方面没有给出时序和环境属性等方面设计

规范,一方面给出了相关的设计约束文件,因此我们不需要进行撰写了,我来看
一下吧:

时序和环境属性的约束:

从上到下依次是:清除以前的约束、时钟的约束、输入端口延时的约束、输入端

口环境属性的约束、输出端口延时约束、输出端口环境属性的约束。

布局规划中,包含的物理信息,对于了相应地物理约束,如下所示:

约束都给我们准备好了,我们就可以启动 DC 了

·启动 DC,进行读入设计前的检查(这里跟之前的章节一样,不再陈述)

·为 formality 创建文件,以便 retiming 转化可以捕捉到相应地文件,总之就是

形式验证要用到,命令如下:

set_svf STOTO.svf

·读入设计和检查设计(很前面的章节已经,这里不再陈述)

·执行时序约束,查看约束是否满足,同时执行非默认的物理约束:
source STOTO.con #Design Constrain

check_timing

source STOTO.pcon #Physical Constrain

report_clock

·根据设计规范,应用不同的优化命令:

-->根据 1 和 2,IO 约束是保守值,能够更改,还有就是最终的设计要满足寄存

器到寄存器之间的路径,因此,我们可以进行路径分组,并且更关注时钟那一组,

也就是寄存器到寄存器那一组,优化的命令如下所示:

group_path -name clk -critical 0.21 -weight 5

group_path -name INPUTS -from [all_inputs]

group_path -name OUTPUTS -to [all_output]

group_path -name COMBO -from [all_inputs] -to [all_output]

然后我们可以查看时候进行了设置:report_path_group,得到结果如下:

-->根据 3,INPUT 模块的结构需要保护;根据 4,PIPELINE 模块需要进行

register_timing,也就是纯的流水线,因此也不能被打散,因此需要设置:

set_ungroup [get_designs "PIPELINE INPUT"] false

设置之后我们需要查看是否设置正确(设置正确会返回 false )

get_attribute [get_designs "PIPELINE INPUT"] ungroup


如下图所示:

ungroup 是取消层次的依次,设置为 true 就是要进行取消层次结构;因此我们

要设置为 false

-->根据 6,I_DONT_PIPELINE 模块的寄存器不能被流水线移动,根据前面的讲

解,我们可以这样约束:

set_dont_retime [get_cells I_MIDDLE/I_DONT_PIPELINE] true

然后检查是不是约束成功,或者约束对了:

get_attribute [get_cells I_MIDDLE/I_DONT_PIPELINE] dont_retime

如下图所示,返回应为 true:

-->根据要求 4,需要进行 pipelined,于是我们可以启用 register_timing,约束

如下所示:

set_optimize_registers true -design PIPELINE

-->根据要求 5,虽然 PIPELINE 进行了 pipelined,也就是进行了寄存器 retiming,

但是输出寄存器不能动,也就是保持原来的寄存器,因此需要约束:

set_dont_retime [get_cells I_MIDDLE/I_PIPELINE/z_reg*] true

然后检查一下是否正确:
-->保存在综合之前保存一下我们的设计:

write -f ddc -hier -out unmapped/STOTO.ddc

·进行综合:

根据要求 8:设计是时序关键的,因此我们要在综合的时候加上-timing 选

项;根据要求 10:要执行扫描插入,因此要加上-scan 选项,看预加上扫描

链综合后是否有违规;根据要求 7、9 以及前面的要求,我们可以加上

-retiming 选项优化进行寄存器、组合逻辑等的优化;综合使用的命令如下

所示:

compile_ultra -scan -timing -retime

·综合后检查与处理:

-->综合完成之后,我们可以查查看我们用了哪些特性(这一步可以忽略):

(这些特性都是大把大把地烧钱啊)

-->查看哪些模块是否被打散,即验证与约束的是否一致:
可以知道:MIDDLE, OUTPUT, DONT_PIPELINE, GLUE, ARITH and RANDOM

这些模块都被打散了; 没有被打散的,也就是保存了模块结构的只有下面的这三

个设计:STOTO, PIPELINE, INPUT

我们从 GUI 中也可以看到:

只有顶层设计 STOTO 和子模块 PIPELINE, INPUT 的边框被保存下来了,其他

的都被打散了,也就是找不到模块的边界了。

-->查看是否有约束违规:

这里我们通过重定义的形式,把生成的时序报告保存到文件中:

redirect -tee -file rc_compile_ultra.rpt {report_constraint -all}


(本实验中,有时序违规)

-->查看时序报告:

redirect -tee -file rt_compile_ultra.rpt {report_timing}

下面我们就来看看这个这个时序报告的一部分吧:

从上面的报告我们可以知道,虽然一些模块被打散了,但是模块的例化名还在,

我们可以通过例化名来找到原件所在的模块,方便我们查看延时不合理时提供定

位;这也就是模块例化名字唯一化的一个好处。

-->保存设计:

write -f ddc -hier -out mapped/STOTO.ddc

综合完成了,此外我们为 formality 进行停止记录数据(总之就是形式验证要做

得事):

set_svf -off
·查看寄存器是否被移动等操作,也就是查看优化技术的结果细节(有兴趣的可

以仔细看下,深入了解)

前面我们进行了各种 retiming、pipeline 的优化,有一些寄存器被移动了,有

些组合逻辑被分割了,我们现在就来看看那些被移动,一方面是纯粹的查看进行

了解一些优化技术,另一方面是看看是否存在约束与预期不符合的情况。

-->查看在 PIPELINE 设计中被 register retiming 技术移动过的寄存器 :

get_cells -hier *r_REG*_S

通过返回值(即返回寄存器的名字)的路径,我们可以知道 PIPELINE 中的流水

线寄存器被移动过了:

(retiming 中被移动过的流水线寄存器的名字以 clkname_r_REG*_S*结束,*是

通配符),再结合我们我们的原理图,我们可以知道,是 z1_reg 被移动了(一

位后缀名是 z1 跟 s1)

-->我们还可以查看例化的名字原来的模块名字,如下所示:
查看原来的 I_IN 模块:

report_cell -nosplit I_IN

-->在第 1 点中我们说只是通过名字中的 1 来说移动的是 z1_reg,这显然是不

够充分,可以通过下面来验证 z_reg 是否被移动过:

get_cells -hier *z_reg*

有返回值,说明这个寄存器存在,没有被移动过(移动过之后就被换了例化名字):

然后我们来查看一下 z1_reg,可以看到找不到对象,说明被移动了:

-->查看其它的被 retiming 移动都的触发器(retiming 中,被移动过的却不是

流水线中的寄存器的被命名为 R_* ):
上面是 INPUT 模块中被 retiming 移动的寄存器,我们可以查看该模块是否有不

被移动的寄存器:

get_cells I_IN/*_reg*

有返回值,说明是存在有不被移动的寄存器的。

-->通过下面的命令:

report_timing -from I_MIDDLE/I_PIPELINE/z_reg*/*

可以知道 PIPELINE 模块是寄存输出的(因为有返回报告值)

优化的实战部分都这里就结束了,最后,DC 的优化命令有很多,不懂的可以通

过 man 命令查看。
九、逻辑综合后的形式验证
这里来讲一下 formality 的使用,貌似跟 tcl 和 DC 没有很强的联系;然而说没

有联系,也是不正确的。在综合完成之后,可以进行形式验证。此外这里不是专

门讲解 formality 的使用的,因此只会简单地实践一下它的用法。

formality 是 Synopsys 公司的形式验证工具,上一节我们得到了综合后的

设计,这里我们就要验证综合后的设计和我们的 RTL 代码是否一致。

1、准备好 RTL 文件、综合优化后的文件以及带有优化映射信息的 SVF 文件:

2、书写相应地流程文件:

3、启动 formality:

fm_shell

对上面脚本不清楚的或者不懂的,可以使用 man 命令查看它的用法:

-->
-->

-->
-->

4、执行我们写的脚本

得到结果如下,说明验证通过了:

You might also like