IC基础(三):设计中常用的时序优化方法
参考书目:英文版:《advanced FPGA design》 中文版:《高级FPGA设计,结构,实现,和优化》
解决数字电路中时序问题的八大忠告
忠告一:如果时序差的不多,在1ns以内,可以通过修改综合、布局布线选项来搞定,如果差的多,就得动代码。
忠告二:看时序报告,找到时序最差的路径,仔细看看是什么原因导致,先看逻辑级数是多少?是哪种电路有问题,乘法器或者RAM接口数据,弄清楚哪儿的问题。
忠告三:搞时序优化的话,插入寄存器是王道,但也要看具体情况,不一定都得插寄存器,插入寄存器效果不明显的话,先检查一下寄存器插入的位置,如果寄存器不是在关键路径的中间插入而是在某一端的话,确实不大明显。
忠告四:把关键路径找出来,看时序报告,看是什么原因导致频率上不去,如果是组合逻辑复杂,就优化逻辑或者复制逻辑,如果是DSP延迟大,就选多级流水的,只要想搞到150,就一定可以。
忠告五:看时序报告的时候,建议同时对照电路图一起看,这样最直观。
忠告六:对照代码,自己把关键路径涉及部分的电路图画出来,然后根据时序要求,算一下要插多少寄存器,插哪儿合适。
忠告七:32bit的比较器,进位链有点长,可以分段比较,分成4个8bit的数据段去比,或者分成两段,先比高16,插寄存器,再比低16,时序很好,如果想深入些,就自己手写一个比较器,不要调库。
忠告八:多bit的逻辑,时序上不去,通常都是进位链太长,通常做法就是打断进位链,建议看看计算方法或者数字算法之类的书,应该会有帮助。
进行数字电路中时序优化的五大方法
这里说的优化是让FPGA设计获得更高的工作频率,也就是通常说的性能和吞吐率。
1.插入寄存器(pipeline)
这种方式会增加设计的时滞(clock latency)。插入了几个寄存器,结果输出就会延长几个周期,在不违反设计规格(对clock latency有要求)以及功能没有影响的时滞情况之下可以这么做。举例说明,如果下面一个FIR滤波器的设计没能满足时序要求。
1 module fir( 2 output [7:0] Y, 3 input [7:0] A,B,C,X, 4 input clk, 5 input validsample); 6 always @(posedge clk) 7 if(validsample) begin 8 X1<=X; 9 X2<=X1; 10 Y<=A*X+B*X1+C*X2; 11 end 12 endmodule
从代码可以看出,X2这条路径过长,是整个设计的Critical Path,如果采用流水线设计可以用寄存器暂存自已在执行Y的运算,改进如下:
1 module fir( 2 output [7:0] Y, 3 input [7:0] A,B,C,X, 4 input clk, 5 input validsample); 6 reg [7:0] X1,X2,Y; 7 reg [7:0] prod1,prod2,prod3; 8 always @(posedge clk) begin 9 if(validsample) begin 10 X1<=X; 11 X2<=X1; 12 prod1<=A*X; 13 prod2<=B*X1; 14 prod3<=C*X2; 15 end 16 Y<=prod1+prod2+prod3; 17 end 18 endmodule
2.并行化设计
并行化设计的思想是将一个逻辑函数分解为几个小一些的逻辑函数并行计算,从而减少关键路径上的延迟 。
例如计算两个8bits数的乘法,将8bits数分为两个4bits数,则乘法运算可以被分解为下面几个部分:
X∗X={A,B}∗{A,B}={(A∗A),(2∗A∗B),(B∗B)};
通过这种方法可以将设计简化为一系列4bits乘法器的实现。
1 module power3( 2 output [7:0] XPower, 3 input [7:0] X, 4 input clk); 5 reg [7:0] XPower1; 6 //部分结果寄存器 7 reg [3:0] XPower2_ppAA,XPower2_ppAB,XPower2_ppBB; 8 reg [3:0] XPower3_ppAA,XPower3_ppAB,XPower3_ppBB; 9 reg [7:0] X1,X2; 10 wire [7:0] XPower2; 11 wire [3:0] XPower1_A = XPower1[7:4]; 12 wire [3:0] XPower1_B = XPower1[3:0]; 13 wire [3:0] X1_A = X1[7:4]; 14 wire [3:0] X1_B = X1[3:0]; 15 wire [3:0] XPower2_A = XPower2[7:4]; 16 wire [3:0] XPower2_B = XPower2[3:0]; 17 wire [3:0] X2_A = X2[7:4]; 18 wire [3:0] X2_B = X2[3:0]; 19 assign XPower2 = (XPower2_ppAA << 8) + (2*XPower2_ppBB << 4) + XPower2_ppBB; 20 assign XPower = (XPower3_ppAA << 8) + (2*XPower3_ppBB << 4) + XPower3_ppBB; 21 always @(posedge clk) begin 22 //第一级流水线 23 X1 <=X; 24 XPower1 <= X; 25 //第二级流水线 26 X2 <= X1; 27 XPower2_ppAA <= XPower1_A * X1_A; 28 XPower2_ppAB <= XPower1_A * X1_B; 29 XPower2_ppBB <= XPower1_B * X1_B; 30 //第三级流水线 31 XPower3_ppAA <= XPower2_A * X2_A; 32 XPower3_ppAB <= XPower2_A * X2_B; 33 XPower3_ppBB <= XPower2_B * X2_B; 34 end 35 endmodule
3.逻辑展平
通过优化掉设计中的优先级译码电路,逻辑结构被展平,路径延迟得以缩短,优先级译码电路常出现在IF/ELSE结构语句中出现。
1 module regwrite( 2 output reg [3:0] rout, 3 input clk,in, 4 input [3:0] ctrl); 5 always @(posedge clk) 6 if(ctrl[0]) rout[0] <= in; 7 else if(ctrl[1]) rout[1] <= in; 8 else if(ctrl[2]) rout[2] <= in; 9 else if(ctrl[3]) rout[3] <= in; 10 endmodule
上面代码综合后就会产生优先级译码器,通过各项平级的if语句或者case语句可以避免这样的优先级译码设计
1 //改进后 2 module regwrite( 3 output reg [3:0] rout, 4 input clk,in, 5 input [3:0] ctrl); 6 always @(posedge clk) begin 7 if(ctrl[0]) rout[0] <= in; 8 if(ctrl[1]) rout[1] <= in; 9 if(ctrl[2]) rout[2] <= in; 10 if(ctrl[3]) rout[3] <= in; 11 end 12 endmodule
4.均衡设计
均衡设计的思想是把 Critical Path 上的组合逻辑拿出一部分放在 short path 上进行,从而缩短 Critical Path 的延迟。
下面举例一个8位加法器。
1 module adder( 2 output reg [7:0] Sum, 3 input [7:0] A,B,C, 4 input clk); 5 reg [7:0] rA,rB,rC; 6 always @(posedge clk) begin 7 rA <= A; 8 rB <= B; 9 rC <= C; 10 Sum <= rA+rB+rC; 11 end 12 endmodule
可以看到,在寄存器rA,rB,rC之前的路径上没有组合逻辑,所以可以考虑把一部分计算拿到寄存器之前
1 //改进后 2 module adder( 3 output reg [7:0] Sum, 4 input [7:0] A,B,C, 5 input clk); 6 reg [7:0] rABSum,rC; 7 always @(posedge clk) begin 8 rABSum <= A + B; 9 rC <= C 10 Sum <= rABSum + rC; 11 end 12 endmodule
5.优化路径
最后一种方法也是我认为最难的,通过优化数据流的路径来缩短 Critical Path ,提升系统性能。重新布局和 Critical Path 在一起的路径,从而 Critical Path上的逻辑门可以更靠近目标寄存器。举一个栗子:
1 module randomlogic( 2 output reg [7:0] Out, 3 input [7:0] A,B,C, 4 input clk, 5 input Cond1,Cond2); 6 always @(posedge clk) 7 if(Cond1) 8 Out <= A; 9 else if(Cond2&&(C<8)) 10 Out <= B; 11 else 12 Out <= C; 13 endmodule
可以看到C作为B的输出条件的路径最长,经过了一个比较器和两个逻辑门,是整个设计的 Critical Path ,可以做后续优化。
1 //改进后 2 module randomlogic( 3 output reg [7:0] Out, 4 input [7:0] A,B,C, 5 input clk, 6 input Cond1,Cond2); 7 wire CondB = (Cond2 &!Cond1); 8 always @(posedge clk) 9 if(CondB&& (C<8)) 10 Out <= B; 11 else if(Cond1) 12 Out <= A; 13 else 14 Out <= C; 15 endmodule
后续待补充。。。