原创 【博客大赛】马克思教我们优化时序之补全if else

2012-6-2 20:07 2108 9 15 分类: FPGA/CPLD

 

时序优化中重要的一项就是提高模块的最高工作频率,工作频率由关键路径决定,通常的提高工作频率的步骤是:利用时序分析工具找到关键路径,分析关键路径主要延迟是布线延迟还是逻辑延迟,然后再轮番十八般武器,如果是逻辑延迟过大就用逻辑切割,插入D触发器,如果布线延迟太长,则复制触发器,减小负载等等等,按部就班后,有时可以明显改善,但很多时候由于设计需求所限不能插入触发器,或是面积受限无法复制触发器,这些程式化的优化方法就起不了作用了,此时,该怎么办呢? 马克思爷爷曾经说过:“世上任何事物都不是孤立的,而是相互联系的,相互制约,相互作用”。受此启发,我们所看到的关键路径,也并不是单单由关键路径上的逻辑决定的,而是由工程里所有的逻辑决定的,其中包括很多和关键路径无关的逻辑。一块FPGA的资源是固定的,如果工程里的一些模块优先占用了风水宝地,剩下的模块只能将就使用其次的资源,最终很委屈的成为了关键路径,所以,关键路径的形成可能并不是由于自身逻辑的复杂,而是好的资源被其他模块抢走了,我们可以换一换思路,不要局限于关键路径,从系统角度出发,通过简化关键路径之外模块的逻辑,提高工作频率
 
最有效的简化一般都要基于具体的工程而言,从需求,功能,实现等大方向做减法,这些方法需要很强的专业背景,不具备通用性,而我想从更具普遍性的编码风格细节入手,谈一谈好的编码风格是如何简化逻辑,提高工作频率的,本篇先来讲讲大家再熟悉不过,但又很容易被忽视的if  else。
 
if else在FPGA工程里可以说是无处不在,在时序逻辑里其主要可以归为两类,一类是补全if else的,主要用于选择器,如下所示,当sel_1有效输出0,sel_2有效时输出1,其他情况都输出0。
always @(posedge clk or negedge rset_n)
begin
    if(!rset_n)
        d <= 0;
    else if(sel_1)
        d <= 0;
    else if(sel_2)
        d <= 1; 
    else
        d <= 0;
end
其RTL图如图一所示,很简单,就是一个选择器加一个D触发器。
 
1.jpg
图一
另一类是没有补全的if else,主要用于锁存数据,如下所示,和上例不同的是,当sel_1和sel_2无效时,会保持上一个状态的输出。
always @(posedge clk or negedge rset_n)
begin
    if(!rset_n)
        d <= 0;
    else if(sel_1)
        d <= 0;
    else if(sel_2)
        d <= 1; 
end
其RTL图如图二所示,和图一相比,多了一个选择器,并将D触发器的输出反馈到了选择器的输入端,实现锁存的功能。
2.jpg
图二
 
从上述可知,补全if else实现的逻辑会比if else不全的逻辑少一条反馈路径和一个选择器,如果是更复杂的应用,后者消耗的资源还会更多。至此,如能根据具体的应用,需要选择器时使用补全的if else,需要锁存功能时,使用不全的if else,那就没有任何问题了,但是,我想重点提醒下,很多人在使用时,对这两种编码风格混淆不清,特别是在一段式状态机里,造成了不必要的浪费,拉低了最高工作频率,下面举一个工作中实际的例子。
 
工程需要跑到200MHz,但在优化前只有174MHz,查看关键路径如图三所示。
3.jpg
图三
 
oe_width到fast_rd_wait_fifo是当前的速度瓶颈,由于设计所限,无法在这条路径内插入D触发器,开启quartus的寄存器复制优化项,效果也不明显...常用的优化方法都试过一遍了,但都没太多疗效。按照开篇提到的马克思爷爷的"万事万物都有联系"的思路,撇开关键路径,查看工程里其他的模块是否有可以简化之处,寻找了良久,找到了一个貌似可以优化的状态机,截取了其中的关键代码如下。
          ....
            case(fast_rd_state)

            fast_rd_st0:
            begin
                if(executive_cmd
                begin
                      fast_rd_busy  <= 1;
                      if(!count_en)
                      begin
                           fast_rd_ok    <= 0;
                           rdaddr_add <= 1;
                           fast_rd_state <= fast_rd_st1;
                       end
                       else
                       begin
                           fast_rd_ok    <= 0;
                           rdaddr_add <= 1;
                           fast_rd_state <= fast_rd_st2;
                       end
                end
                else
                begin
                      fast_rd_busy  <= 0;
                       fast_rd_ok    <= 0;
                       rdaddr_add <= 0;
                      fast_rd_state <= fast_rd_st0;
                end
            end

            fast_rd_st1:
            begin
                   rdaddr_add <= 0;
                   if(rd_count_almost_full)
                   begin
                       if(fifo_wrusedw < fifo_rd_deep) 
                       begin
                             fast_rd_busy  <= 0;
                             fast_rd_ok    <= 1;
                             fast_rd_state <= fast_rd_st0;
                       end
                       else
                       begin

                        //fast_rd_busy  <= 1;       /*注意这句话*/
                              fast_rd_ok    <= 0;
                              fast_rd_state <= fast_rd_st3;
                       end
                end
                else
                begin
   
                  //fast_rd_busy  <= 1;         /*注意这句话*/
                         fast_rd_state <= fast_rd_st1;
                end
            end
           .....
 
 
注意红色加粗部分,优化前是没有这两句话的,所以优化前,这是一个没有补全的if else,会综合出锁存器,当初没有加这两句话的原因是:从逻辑上感觉没有必要,因为从fast_rd_st0状态跳到fast_rd_st1状态后,fast_rd_busy肯定是1了,没有必要再在fast_rd_st1状态机里加上fast_rd_busy  <= 1,让其保持就可以了,而且这样还可以省两行代码,实际执行的结果也都正常。
 
但是,实际应用根本不需要这个锁存器,在红色加粗部位补上 fast_rd_busy  <= 1后,状态机变成了补全的if else,奇迹发生了,综合频率提高到了190MHz,虽然还没达到200MHz,但相比没有补全if else的写法,提高了16MHz,再看关键路径报告如图四,oe_width到fast_rd_wait_fifo的关键路径悄无声息的消失了。
4.jpg
图四
 
这再一次的证明了马爷爷的思想是多么的神奇和伟大,没有使用任何传统的优化方式,仅仅是改变了其他逻辑的编码风格,就显著的提升了工作频率,虽然从程序上看,fast_rd_busy并不直接属于关键路径的逻辑范畴,但fast_rd_busy牵扯到的逻辑资源和关键路径的逻辑有着紧密的间接相互作用关系,继续查找频率提升的原因,用Technology map viewer查看没有补全if else的fast_rd_busy的源端逻辑如图五所示。
5.jpg
图五
 
再看看补全if else情况下fast_rd_busy的Technology map viewer如图六所示。
6.jpg
图六
 
比较图五和图六,前者具备锁存功能,将fast_rd_busy的输出经过复杂的判断比较逻辑又反馈到了寄存器的输入端,牵扯到的逻辑要明显多于后者,补全if else后,只有选择功能,省去了输出反馈的逻辑,自然就改善了整体的工作频率。
 
使用if else时,我们常常会因为其简单,而混淆了带锁存器和无锁存器这两种编码风格,更甚,在误用了没有补全的if else的写法后,会因其表面的逻辑功能正常和减少的代码量而觉得不以为然,其实,它都在潜移默化的影响着你工程的关键路径,赶紧看看你的工程里是否存在这些没有必要的锁存器吧,统统去掉,定有惊喜等着你奥。
 
 
PARTNER CONTENT

文章评论6条评论)

登录后参与讨论

用户418224 2013-5-6 14:11

在always开始后,就将所有输出都赋予默认值,这样就省得后面补全了。这个方法觉得怎样?

用户403664 2013-1-9 09:05

这次图片怎么挂了?

用户380541 2012-8-18 09:58

写的真好,图文并茂

用户192767 2012-5-31 11:31

俺们实验室的代码规范就是不能省略,以前只知道会生成不必要的锁存器,减少资源用量,LZ介绍的蛮详细

用户419742 2012-5-30 20:59

谢谢编辑的支持鼓励,俺会继续努力的。

用户403664 2012-5-30 11:07

博主所有参赛博文,光看排版就很赞!文章质量一直保持高水准啊~

用户377235 2012-3-28 22:55

学习!

用户403664 2012-3-28 08:27

图片看不到~
相关推荐阅读
用户419742 2016-06-16 11:06
【博客大赛】cache,你给我听话点
在上一篇“为什么程序越优化越慢?”里,详述了程序指令在cache里发生冲突后另运行效率变的完全不可预测的问题,并提出了两种将不老实的cache变乖的良方。本以为cache已经完全被驯服了,没过多久...
用户419742 2013-05-14 20:57
[博客大赛]程序为什么越优化越慢?
正在开发一个基于Nios II内核的项目,使用的开发环境是nios for eclipse,编译器是GCC,整体功能实现后,开始优化速度。默认没有开启gcc的优化选项,一段关键函数Key的运行...
用户419742 2012-06-13 21:33
再诡异的现象背后可能只是一个傻x的低级错误——谈调试心态
  今天调试一个小模块,FPGA的24号引脚作为输入端,在此引脚上外部给一个恒定的0电平,理论上程序应该一直读为0电平,在开机的前10s,程序内部读取该引脚为0,可是10s后始终读取为1...
用户419742 2012-05-24 21:09
【博客大赛】TimeQuest约束外设之诡异的Create Generated Clocks用法
最近在altera FPGA里设计一个外设的驱动模块,模块本身逻辑很简单如下图所示,但是模块和外设之间的时序约束问题搞的很头疼,今天先讲讲总结的一些Timequest下外设约束方法,特别是那毫无用...
用户419742 2012-05-18 20:45
【博客大赛】TimeQuest之delay_fall clock_fall傻傻分不清楚
  这篇我想分享一个之前在用TimeQuest约束双边沿模块的input delay时犯得一个错误,有人看了可能会觉得傻傻的,什么眼神,delay_fall和clk_fall怎么会分不清呢,字...
EE直播间
更多
我要评论
6
9
关闭 站长推荐上一条 /3 下一条