时序收敛地最佳实践

http://blog.ednchina.com/coyoo/378423/message.aspx

Altera的QuartusII提供了物理综合等等优化工具,之前的文章中本人试图进行过介绍,我们建议客户最好是在设计无法通过(或者表面上)修改代码来进一步优化的时候才采取物理综合。

       那么如何真正做到时序收敛地最佳实践呢,首先来看看一般的设计流程:

——>制定设计指导文件,我们也可称为总体规划文件

——>设计功能性代码,即coding

——>进行功能仿真

——>编译设计

——>时序分析,查看设计是否满足时序收敛要求

——>假如未设计未满足时序收敛,调整QII设置,譬如物理综合等

——>重新编译,或重复上一步直到到达要求

以上是很多FPGA工程师(或者是刚学习FPGA设计工程师)经常使用的设计流程,本身在FPGA的资源很充足的情况下该设计流程没有什么值得诟病的,但是我们要提出的是要养成良好的设计习惯,正确设计调试方法。通常工程师拿到设计任务以后一通埋头coding,很快代码就coding结束了,功能仿真也通过,但是一到最后“实现”的时候出现时序收敛问题,而往往这种时候大部分人缺乏分析、修改code的勇气。工程师不愿意更改代码的理由一般是:

——>更改代码的风险

——>项目进度不允许

——>没有时间再对更改后的代码进行验证

——>反正通过调整QII设置可以达到目的

——>反正FPGA是可编程的

要进行最佳的时序收敛设计,就要摈弃上述想法,因为通过调整设置达到要求只是低级的优化,虽然达到要求,但是显然缺乏可移植性。

 

        衡量一下得失,我们认为这种时候修改代码常常是你最好的选择,打个不恰当的比方,工具设置(或者编译设置)就是设计的“绝招”,呵呵,这里的绝招不是武侠里的很厉害、一招毙命的招,而是走投无路的招,绝路之招。一旦确定更改代码,首先要评估代码修改的可能性,其次修改代码后配合一些工具设置。现在回过头来,我们建议在coding之初工程师应该计划或者规划系统的时序收敛问题,这是很关键的,所谓磨刀不误砍柴工嘛,减少反复时间。

        如何真正做到最佳时序收敛实践呢,试图分两大块来介绍。

 

        一、编写“Timing Frindly” HDL代码的小贴士

 

1、计划及规划

——>设计更改的代价一般都很大,特别是越到临近设计周期的后期,所以设计之前要有很好计划和分析。

——>规划好各个模块,尤其各个功能模块的架构。

——>构建合理的设计层次结构。

——>分割好功能边界。

——>建议使用增量编译。

——>要适应因为那些不可预见的原因而常常更改设计,比如设计指导的更改,早期的设计原则不可能做到完美,甚至还会有黑盒子。

想起了那部电影-《预见未来》

 

2、“手中无剑,心中有剑”

        进行coding的时候,时刻记得你所写的代码要实现的实际硬件电路是什么,不要停留在行为级,要深入到数据路径、寄存器等。通常好的代码会给QII更大的自由来进行优化,因此也会有更大的机会达成你的设计需求。

 

3、“理论联系实际”

        设计需要和目标器件的匹配,当然首先根据设计需求选择合适的器件;其次是分析出你的设计将会需要哪些资源,并且需要多少;最后是选定好器件后,根据所选器件的资源情况规划调整设计层次结构。

 

4、其他通用的设计推荐

        还有其他一些设计推荐,比如采用同步设计原则,不使用gate clock和ripple clock转而使用时钟使能、altclkctrl函数或者寄存器。再比如QII handbook上的HDL coding guidelines章节,还有就是对各个小模块进行单独的仿真验证,要对设计进行合理的时序约束。

        另外,我们讲到规划设计的层次结构很重要,到底为什么很重要呢,首先合理的层次结构划分有利于使用增量编译,其次是逻辑设计更利于优化布局,最后就是一旦需要修改代码,修改某个block总比修改整个设计要简单。

        最后一点非常重要的就是,要关注QII的警告信息。举个简单的例子,QII物理综合有时候能达到改善时序收敛问题,但是工具是如何做到的呢,编译警告或者报告信息会给出端倪,根据这些信息常常可以指导我们进行所谓的code changing,而code chang会使得我们可以把这些“绝招”藏起来不使用。

 

二、修改代码改善timing issue

        什么情况下可以通过修改代码达到我们的目的呢?有下面几种情况,我们可以考虑是否可以修改代码来改善:

——>关键路径太长,可以通过TQ来查看时序问题路经的级数

——>高扇出

——>出现与复位相关的问题

——>编译时间异常长

前头我讲过要做到手中无剑心中有剑,假如设计者在code的时候没有考虑到实际电路硬件的映射,写代码比较飘逸(如象写c代码一样),很可能会导致QII软件花费更多的努力来优化你的代码以期达到设计目标,同时导致更多的编译时间以及消耗更多的资源。在众多的可解决timing failure的因素中,这里只讨论HDL change。

 

1、关键路径过长

        Too many logic stages in the critical path。在TimeQuest中可以看到某条路径的logic levels,级数过多必然带来路径延时过大,导致数据到达时间过晚。下图显示了如何在TimeQuest中查看某条路径的logic levels:

点击看大图

上图中有一条有时序问题的路径logic level是6,不能单单根据数字大小来判断逻辑层次过多,要根据具体设计来判断。来看另外一个例子:

点击看大图

两个寄存器之间逻辑层数达到了24级。

        那么如何解决逻辑层数过多的问题呢,首先要分析设计意图,假如设计可以不必要求单周期内完成,则可以考虑加入multi-cycle来约束这些路径。如果必须要求一个时钟周期完成,则需要考虑修改代码加入pipeline,后面会有例子演示。为了避免插入pipline寄存器,设计者也可以考虑更改代码使代码更有效率,稍后试着给出一个例子。最后就是采用register retiming,也就是物理综合中的一种,“绝招”带来的后果的是编译时间增长。

1)Pipelining

        直接给出一个例子来看看如何加入pipeline,我来看下面这张图

点击看大图

上图上半部分两个寄存器之间有8级逻辑,在中间加入一级流水以后寄存器之间的最大逻辑层数为4,如上图的下半部分所示。上图只是一个示意,具体到代码中怎么实现呢,请看下面这个例子

点击看大图

从上述代码中我们看到,输入信号signa、signb.....到signg为8bit向量,而中间变量prod1、prod2......到prod6为这些8bit输入向量的乘积和。再来看输出变量outsig,它的取值取决上述变量。可以看到输入变化带来中间乘积和变量的变化,而到下一个时钟沿输出要基于所有这些变化来更新取值,非常遗憾的是prod*很难在一个时钟周期内完成。我们来看看TimeQuest的分析

点击看大图

我们看到最差路径是从sigd第二bit到outsig的第14bit,之间有24层逻辑。该如何加入pipeline呢?如下所示

点击看大图

上述代码修改成让输出不是纯组合逻辑输出,而是寄存器输出。再来用TQ看上述代码的分析结果:

点击看大图

刚才寄存器之间逻辑层数又24变成了13.

 

2)、避免不必要的优先级编码以减少逻辑层数

        其实就是能用case的地方尽量不用if...else语句,看看下面是用if...else的结果

点击看大图

假如我们改用case语句呢:

点击看大图

点击看大图

 

2、高扇出信号处理

        在FPGA中信号是被buffer的,因此寄存器高扇出造成的时序失败更甚于布局。查看寄存器的扇出,有很多种方法,比如编译报告、TimeQuest或者Chip Planner。下面一个例子扇出4108带来4.429 ns 的interconnect delay

点击看大图

        那么如何处理因为高扇出带来的时序问题呢?推荐一些解决方法

——>通过使能工具中逻辑复制选项直接减少fan-out

——>指导高扇出节点布局到离其驱动目标更近

——>使用max_fanout约束

——>手动逻辑复制

手动在代码里复制逻辑往往会被综合工具优化掉,所以需要加入preserve属性约束,下面是一个逻辑复制的示意

点击看大图

其他关于处理高扇出的方法有,比如将高扇出信号指定使用全局布线资源以及使用物理综合选项等。

 

3、设计安全的复位

        一般一个系统的复位分为两种即同步复位和异步复位,关于什么是同步复位和什么是异步复位这里就不做介绍。

        从Altera的观点来看,似乎不推荐使用同步复位,找到了一些Altera给出的理由,同步复位信号复位整个设计同步于时钟,那么假如复位信号没有在同一时刻将设计中所有寄存器复位,那么设计就会产生故障。为什么呢?我们想象一下,复位信号在这里是不是就是一个高扇出信号,布线的时候需要布满到整个芯片,由于布线延迟的存在,很有可能复位信号到达各个寄存器的时间不一致,就会产生上述问题。另外一个altera不推荐使用同步复位的理由是同步复位占用更多的资源,而且编译的时候复位信号会当作数据来处理,所以才出现上述到达时间问题。

        相对来说,Altera似乎更推荐使用异步复位。为什么呢?因为Altera的FPGA有到各个寄存器专用的全局布线资源,这些布线资源可以用来异步地复位寄存器。当然,在使用异步复位的时候,Altera还是强烈建议客户注意处理异步复位信号的释放,即让异步复位信号同时释放(异步复位同步释放)以避免一部分寄存器中前一个时钟沿释放而另一部分在后一个时钟沿释放复位。

        我们来看看异步复位同步化的实例:

点击看大图

在用TimeQuest做时序分析的时候,一定要注意由于reset_n到reg3和reg4路径是异步路径所以要将其设为false paths,而从reg4到reg1和reg2的时序路径是可以分析的,分析其recovery或者reomal。下图是上述电路的波形图

点击看大图

上图可以很清楚看到,一个异步信号进入系统,clock_domain_a时钟域在同一个时钟沿移除复位状态。这里需要注意的是当系统涉及到多个时钟域的时候,需要为每个相关时钟创建此类复位结构。

 

4、推导Memory

        Altera提供各种存储器函数,但是有时候设计师喜欢自己来设计自己的存储单元,工具在从HDL推导存储器的时候会根据不同器件的存储资源来进行。也就是说由于器件的差异有可能会导致相同的设计中不同的器件中实现的时候会有所差异。

        我们知道Altera不同器件中内嵌了不同的存储器颗粒,在HDL设计的时候尽量根据这些颗粒的尺寸来设计你的存储器。如果设计不合理工具推导的时候可能不会使用内嵌的存储器颗粒,会推导寄存器,这样也许会达不到你的设计性能要求。

        具体altera的内嵌RAM以及设计中有哪些要求可以查询Altera的相关手册,这里仅给出一个例子,比如Altera器件不支持复位的时候初始化存储器,所以不要在复位的时候去初始化memory如RAM,但是有些器件是支持用mif文件来初始化memory。请看下述代码

  reg [7:0] mem [0:31];

  integer i;

  always @ (posedge clock or posedge reset)begin

    if (reset == 1'b1)

       mem[address] <= 0;

    else

    if (we == 1'b1)

       mem[address] <= data_in;

 

       data_out <= mem[address];

    end

  end

这段代码实现的是一个32X8的memory,我们看到这里在复位的时候给该memory进行了初始化即清零,所以该段代码在编译的时候不会被推导使用器件内的memory blocks。

 

5、过长的编译时间

    有时候碰到工具编译时间过长,你就应该怀疑是不是HDL中隐藏有什么问题。一般检查代码中是否出现下面一些情况

——>代码中包含组合逻辑环

——>设计了大尺寸RAM结构无法用memory block来推导

这时候最好将代码进行改进。可以举一个组合逻辑环的例子

 

always@(sig1 or sig2 or reset or int1 or int2 or int3)  begin

if (!reset) begin

  int3 <= 0;

end else begin

if (sig1 > sig2) begin

      int1 <= sig1 - sig2 ;

   end else begin

      int1 <= sig2 - sig1 ;

   end

   int2 <= sig1 + sig2 ;

   int3 <= ( int1 + int2 + int3 );

   end

end

这段代码如果用QII来编译你会看到以下信息

 

Warning: Found combinational loop of 4 nodes

    Warning: Node "Add4~17|datab"

    Warning: Node "Add4~17|combout"

    Warning: Node "int3[8]~16|datab"

    Warning: Node "int3[8]~16|combout

有时候这些警告信息要引起你足够的注意,稍不留神就有可能导致你设计的时序出现问题。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值