数字IC/FPGA设计中的时序优化方法

描述

引言

在数字IC/FPGA设计的过程中,对PPA的优化是无处不在的,也是芯片设计工程师的使命所在。此节主要将介绍performance性能的优化,如何对时序路径进行优化,提高工作时钟频率。

好的优化就应该是在代码书写之前或书写时进行考虑,设计完成再进行优化,只能是修改代码。即便如此,修改代码优化设计速度也是值得提倡的。本文讨论了如何减少关键路径延时、逻辑复制、插入寄存器增加流水、寄存器平衡、使用并行结构以及消除代码中的优先级等优化设计速度的方法。

一. 组合逻辑中插入寄存器(Pipeline) + 重定时(retiming)

在FPGA或者是数字IC设计中,优化时序最常用且效果最明显的方法就是在组合逻辑中插入寄存器,也叫做插入流水线。如下图所示。

时序

该方法额外插入寄存器增加的时钟周期延时并不会违反整个设计的规范要求,从而不会影响设计的总体功能性实现,也即额外插入的寄存器在保持吞吐量不变的情况下改善了设计的时序性能,随之带来的是面积的增加,因此需要权衡好面积和时序要求。

很多同学可能会疑惑,我应该在哪里插入寄存器比较好呢?插入寄存器的方法我认为有两种,一种是人为评估组合逻辑的长度后在组合逻辑中插入寄存器,另外一种就是直接在组合逻辑的输出直接插入寄存器再由EDA工具自动retiming重定时,如下图。

时序

什么是retiming重定时并且它的作用是什么呢?retiming就是人为或者是工具在不增加寄存器个数的前提下,通过改变寄存器的位置来优化关键路径,起到了平衡寄存器件的组合逻辑长度的作用。

注意的是有时候并不需要插入寄存器直接进行retiming也能一定程度上优化时序路径

时序

DC如何启动retiming

方法1 自适应retiming(Adaptive retiming)

Adaptive retiming一般是处理常规设计,关键路径的组合逻辑基本上是只会移动到相邻的寄存器。

 

compile_ultra -retime

 

方法2 流水线retiming(pipelined retiming)

Pipelined retiming主要处理pipelined设计,当关键路径比较长时,路径多打几拍。综合时,采用pipelined retiming,会将路径分摊到多个寄存器中,进而提升频率。Pipelined retiming一般针对某个模块进行retiming。 相应的DC指令如下所示。

 

# set_optimize_registers 设置需要retiming的designs模块,可以使用通配符*set_optimize_registers true -design [get_designs piped_adder*] compile_ultra

 

NOTE:对于retiming对设计的变动比较大,需要特别关注formality是否通过,同时在设计前期比较推荐只对模块进行retiming,确保设计的一致性。

二、组合逻辑平衡(操作符平衡)

操作符平衡就是通过合理使用括号来对逻辑进行分组,通过这种技术可以增加设计性能,平衡所有输入到输出的延时,而整个设计的功能并不会改变,代价是可能增加部分面积。如下图所示,通过合理使用括号,平衡乘法操作符,使得输入到输出的延时从三级乘法操作减少到两级。

时序

三、适当进行逻辑复制以优化设计速度

逻辑复制用于当某个信号的扇出比较大时,会造成该信号到各个目的逻辑节点的路径变得过长,从而成为设计中的关键路径,为了解决这个问题,可以通过在书写代码的时候对该信号进行复制,以达到“分担”信号扇出过多的目的。逻辑复制的时候也分为组合逻辑复制和寄存器复制,下面例子是将对组合逻辑复制。

原电路

module ...

wire temp;

assign temp = a & b & c;

always @(posedge clk)begin

q1 <= temp;

if(en)

q2 <= temp;

end

endmodule

时序

逻辑复制后电路

module ...

wire temp1;

wire temp2;

assign temp1 = a & b & c;

assign temp2 = a & b & c;

always @(posedge clk)begin

q1 <= temp1;

if(en)

q2 <= temp2;

end

endmodule

时序

可以看到逻辑复制后,组合逻辑的扇出由2变成了1,能够降低组合逻辑的延迟。当然寄存器复制也是类似的。

四、通过消除代码中的优先级优化速度

根据 Altera器件的特点,一般if-else 嵌套长度不要超过7级。那如何通过消除代码中的优先级来优化设计的速度?所谓消除优先级,就是说设计功能可以通过无优先级方式来实现,对于那些对优先级有要求的功能模块无法使用这个技巧。

有优先级的写法

module ...

always @(posedge clk)begin

if(sel=4'b0001)

sig_out <= 4'b0001;

else if(sel=4'b0010)

sig_out <= 4'b0011;

else if(sel=4'b0100)

sig_out <= 4'b0101;

else if(sel=4'b1000)

sig_out <= 4'b0111;

else

sig_out <= 4'b1001;

end

endmodule

时序

无优先级的写法

假如说上述代码无优先级要求,可以使用并行的case语句将代码修改为:

module ...

always @(posedge clk)begin

case(sel)

4'b0001: sig_out <= 4'b0001;

4'b0010: sig_out <= 4'b0011;

4'b0100: sig_out <= 4'b0101;

4'b1000: sig_out <= 4'b0111;

default: sig_out <= 4'b1001;

endcase

end

endmodule

时序

 

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分