九、数字集成电路设计,DC综合, 层次化设计的编译与后综合过程

1. 层次化设计的编译过程

一个层次化设计的编译过程包含两个阶段——

  1. 将所有的子模块映射到门级
    在这里插入图片描述
    从上图可以看出,D_design 含有三个不同的模块,在编译的这个阶段,U1 、U2 、 U3 分别由 RTL 级映射到门级,并且各个模块之间的层次关系保持不变。在映射的过程 中,设计约束都暂时没有考虑。
  2. 优化
    在这个阶段,Design Compiler 根据各个子模块的时序和面积约束对它们分别进行优化,并且在优化的过程中要考虑到不同子模块周围的环境,修正产生的错误。在这里插入图片描述

2.多次例化模块的编译

在一个层次化的设计中,我们可能会遇到下面这种情况——
在这里插入图片描述
上图中,被综合的模块中 D_design 中含有三个子模块 U1 、U2 和 U3 ,其中 U1 和 U3 都是由模块Ades 例化而来,这里的Ades 称为多次例化的模块。对于这样一个设计,在compile 之前使用 check_design 作检查的时候会报一个 warning ,即设计中存在多次例化的模块 (multiple instantiations) ,如果在这种情况下,我们不考虑多次例化的模块(Ades) ,那么在继 续的 compile 时候程序会终止退出。因此,必须对它进行处理,这一节里我们介绍两种方法 ——uniquify 和 compile + dont_touch。

  • 方法一:uniquify
    使用 uniquify ,DC 会对每个例化的模块作一份拷贝,然后对它们分别取一个名字, 即把不同的例化模块当作不同的两个模块处理——
    在这里插入图片描述
    注意看上图,U1 和 U3 两个模块的设计名分别由原来的 Ades 变成了 Ades_0 和 Ades_ 1 ,因此在编译时,DC 会将它们当作两个不同的模块,这样就可以根据它们不同 的周围环境作优化。
    使用 uniquify 的具体实现方法如下——
    在这里插入图片描述
    这段脚本与以前的脚本只有一处不同,即在 compile 之前加上 uniquify 这一行。
  • 方法二:compile + dont_touch
    这种方法先将多次例化的模块作单独的约束和编译,然后在整合到上一级模块的过 程中将它的属性设置为 dont_touch ,再编译。
    在这里插入图片描述
    上图中,U1 和 U3 两个模块的设计名都没有变化,只是在编译 D_design 之前先将 Ades 编译一次。这样U1和 U3 实际上是一模一样的模块。
    compile+dont_touch 的实现方法如下——
    在这里插入图片描述
    这里的约束文件有两个,一个是 Ades 的 Aconstraints.tcl ,另一个是 D_design 的 Dconstraints.tcl , 并且在 source 后 一个 约束文件之 前要对编译过 的 Ades 设置 成 dont_touch。
    在设置了 dont_touch 属性之后,编译 D_design 的时候就会忽略 Ades ,这样有好处 也有坏处,好处是可以保护模块不被修改,但是这样同时也限制了 DC 对 U1 和 U3 的进一 步的优化。
  • uniquify Vs compile + dont_touch
    通过对上述两种方法的介绍,我们不难看出它们各自的优缺点——
    compile+dont_touch 由于只需要对多次例化的模块编译一次,因此可以减少整个设 计的编译时间,也可以减少内存的使用量。在多次例化的模块很复杂并且工作站的硬件 条件有限的情况下,使用这种方法的优越性的比较明显的。还有,如果这个 Ades 是一 个第三方提供的硬核(hard-core) ,那么我们也只能使用这种方法。
    使用这种方法的缺陷也是显而易见的: 由于顶层模块在编译的时候 Ades 设置了 dont_touch ,这就妨碍了 DC 针对 Ades 的各个实例周围环境的不同的进一步优化,从而 使得结果不能真实的反映各个实例周围的环境变化。
    uniquify 由于把各个多例化模块作为独立的模块来看,因此 DC 可以分别针对它们 作出更好的优化,从而得到的结果也是比较理想的。缺点就是编译的时间稍微较长,但 是对于一些不大的模块来说,这些是可以忽略的。
    正因为 uniquify 可以综合出更好的结果,所以如果一般推荐使用 uniquify 解决多例 化模块的综合问题。

3.编译一个大型设计

对于一个大型设计而言,由于模块规模的扩大,编译时间也相应的变长,要长达几个小 时甚至超过一天,这样的时间对于讲究”Time to market”的设计者是比较重要的。因此就更加 注重编译的技巧,本节我们主要讨论下面三个方面的技巧——

  • 编译层次化设计的技巧
  • 第二次(Second-pass)编译技巧
  • characterize

1. 层次化编译

对一个大型设计来讲,有两种层次化编译技巧—— 自上而下(Top-down)以及自下而上 (Bottom-up) 。 自上而下的方法是指将整个设计一次性读入,施加顶层约束后直接进行编译; 自下而上的方法则先一个个编译比较底层的子模块,给它们加入时序和负载预算,然后在顶 层将各个子模块整合起来。

  • 自上而下(Top-down)
    在这里插入图片描述
    上图是自上而下编译方法的具体步骤,可以看出假如顶层设计是RISC_CORE 这个 模块,则先直接将它读入,然后处理多次例化的模块,施加顶层约束后就直接编译。它 的代码基本上如下所示——
    在这里插入图片描述
    自上而下的编译方法有一个明显的优点,即它使得设计者无需考虑各个子模块之间 的依赖关系 ,也就不需要制定子模块之间的时序和负载预算 ,这一切都由 Design Compiler 自动考虑。另外,使用这种方法也使得设计者编写脚本变得简单,维护起来也 比较方便。
    在介绍自上而下的编译方法的时候,我们还要顺便提及 DC 编译的一种模式—— Simple Compile Mode(简单编译模式)。

这种模式在设计没有严格的约束的情况下能取得较快的编译速度,另外多例化模块 的处理也自动进行。下面是 RISC_CORE 的 Simple Compile Mode 脚本——
在这里插入图片描述
可见 ,使用这种模式省去了 uniquify 这句, 同时编译之前要先设置一个变量 set_simple_comile_mod。

  • 自下而上(Bottom-Up)
    自下而上的编译方法其步骤如下图所示——
    在这里插入图片描述
    和前一种方法不同, 自下而上的编译方法需要先单独编译各个子模块,在编译子模 块的同时要考虑到与其它模块之间的关系,看是否满足约束,然后再读入顶层文件,施 加顶层约束,顶层编译完成之后还必须看顶层约束是否满足。下面是单个模块编译的脚 本——
    在这里插入图片描述
    下面是顶层模块编译脚本——
    在这里插入图片描述
    从上面的过程不难看出Bottom-Up 方法的一些特点——
    优点是利用了”分而治之”的策略,这对于大型的不可能一次编译的设计是十分有用
    的;另外它也摆脱了 Top-down 方法的对工作站硬件条件的限制,使得大型设计也能在 一般的机器上编译完成。
    缺点是实现步骤比较多,尤其对各个模块之间的时序和负载预算要求很高,如果不 注意会很容易造成违反。
    综合上述两种方法,我们可以做一个小结:对于规模不算太大的设计,我们推荐使 用 Top-down 的编译方法,这样可以在不长的时间内得到满意的结果。
    对于其他需要Bottom-Up 的设计,我们必须确认时序负载预算能很好的反映实际的 工作情况。

2. 第二阶段编译

第二阶段(Second-Pass)编译是指当第一阶段(First-Pass)编译出现违反之后,分析违反原 因从而重新编译的过程,对应的还有第零(Zero-Pass)阶段编译。
关于第二阶段编译,前面的编译策略一章中有比较详细的介绍,前一章介绍的 Top-down 的第二阶段编译的步骤主要有——

  • 检查模块划分
  • 检查约束脚本
    *用更高的 map_effort 编译——compile –inc –map_effort high
    这一节中,我们主要讨论用 Bottom-Up 方法编译后出现违反的情况—— 重新编译顶层模块
  • 重新编译顶层模块
    这种方法是在Bottom-Up 出现顶层模块时序违反的情况下采用的,具体的命令如下
    在这里插入图片描述
    这个命令仅仅修正顶层子模块之间的路径,因此速度会比 compile –inc 更快。
  • 修正设计预算(Design Budget)
    设计预算对于Bottom-Up 的方法来说是至关重要的,在预算的时候,我们都尽量能 收紧(Tighten)每一个子模块时序、负载和驱动的预算,例如我们在最初介绍设计预算的 时候,举的例子是给本模块留整条路径的 40% ,因此模块之间能够空出 20%的裕量。在编译子模块的时候能尽量做到满足预算的要求。这样最后整合顶层设计的时候就不会出 现大的问题。

如果出现问题了,一个方法就是调整预算脚本。看看施加的约束是否与综合后的电 路相吻合。下一节,我们将介绍调整设计预算的一个很有用的命令——characterize。

3.characterize

Characterize 这个命令用于映射到门级的子模块,作用是计算出该子模块周围的环境(延 时、负载和驱动) ,并将得到的实际值作为该子模块的新的约束。如下图一个例子——

  • 使用 characterize
    在这里插入图片描述
    由于整个设计已经映射到了门级,因此这个例子可以计算出子模块 U2 周围的输入 输出延时、输入驱动和输出负载的实际值。然后将这些实际值施加在 U2 模块中,作为 U2 的新的约束。这种方法有点类似于给 U2 的周围照了一张照片。U2 施加了新的约束 之后,就可以在这个基础上做一次高级别的编译。
    通过 write_script 命令,我们可以看到 characterize 之后到底照下了哪些信息——
    在这里插入图片描述
    characterize 给我们提供了一种比较好的第二次编译的方法,加入一个子模块所占的 延时很重, 就可以在保持其他模块不动的情况下将这个子模块重新编译一次,当然重新 编译可以从 HDL 代码开始,下面是一个例子——
    在这里插入图片描述
    这个例子和前一个例子的不同在于,它没用 compile – inc high ,而是直接将它从内
    存中删除,读入它的源文件重新编译,这样可以取得较上一种方法更好的结果。

  • characterize 的局限性
    characterize 无疑向我们提供一种较好的子模块二次编译方法,但是同时它也有一定 的局限性,在使用的时候务必要注意——
    首先,它要求所有的模块必须映射到门级,这是使用 characterize 的一个前提。
    其次,characterize 只能一次对一个子模块使用,即给 U2 作 characterize 的时候 U1 和 U3 模块必须保持不变,否则 U2 得到的环境就不是确定的值。
    再次,characterize 将外界环境直接作为它的约束,这使得它和其他的子模块之间不 存在任何裕量(margin) ,这些裕量全部被该子模块吸收。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chy_wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值