- PART1 引子
- PART2 ISE 综合选项设置
- PART3 代码风格
- PART4 高速电路的设计方法
- PART5 结语
布局布线以后,静态时序分析的结果和自己的预期相差很远,和综合后XST的估值也相差很远。时延里面,route时延占了绝大部分(logic占20%,route占80%)。
恶补了一些资料,给自己的设计总结了三个可能的问题:有些控制信号的扇出太大,没有做位置约束,不好的代码风格。
决定在这里记录自己通向时序收敛的过程。这不是一条平坦的路吧。
这里想说一下我对综合选项的设置。设置的依据是ISE的帮助,网上的资料,以及自己的理解。请大家能为我指正设置得不合理的地方。没有提及的选项采用缺省设置。采用的EDA软件是ISE 13.2,综合器为XST。
【Synthesis Options】
Use Synthesis Constraints File & Synthesis Constraints File:
一般来说,会在implement之前,采用UCF文件对设计进行时序约束。实际上,在synthesis之前,可以先采用XCF文件对设计进行时序约束,以使XST针对时序约束进行synthesis,在synthesis时产生更好的网表。按照网上的说法,XCF中的时序约束应当要比实际需求更紧一些。
XCF文件的实际效果嘛。我谈一下自己使用XST的情况吧。XST完成综合(synthesis)后,会产生一个估计的最大工作频率。某一次综合后,我使用XCF文件将最大工作频率约束的比XST的估值大一些,再重新进行综合后,XST给出的估值确实增大了少许。大部分时候,在重新综合后是看不到效果的。至于对最终结果的影响,就不得而知了。
XCF文件的语法与UCF文件完全一致。与UCF不同,ISE没有为XCF提供编辑的工具,只能自己用文本编辑器编辑。
Keep Hierarchy:
这个选项是设置是否在synthesis与implement中打破设计的层次结构。选项【yes】和【no】很容易理解。选项【soft】的意思则是在综合时不打破层次结构,而在之后打破层次结构。
个人理解。打破层次结构后,有些信号就变了,不利于分析与约束。而打破层次结构,更有利于电路的优化。
【HDL Options】
FSM Encoding Algorithm:
有限状态机的编码方式。我采用了【One-Hot】(独热码)。其优缺点相信大家都非常清楚,不再赘述了。
Case Implementation Style:
case语句的实现方式。使用verilog时,缺省状态下XST不会把case语句综合成你想象的结构。这点,大家可以自己写一段简单的代码试试。
以一个独热码状态机为例:
以下是代码片段: reg [2:0] sta; case(1’b1) sta[2]: …; sta[1]: …; sta[0]: …; endcase |
首先,综合出来的电路不止判断一个比特。XST不知道sta只会出现3’b100、3’b010、3’b001三种可能,它会把诸如3’b101这样的状态也考虑在内,大概把电路综合成如下的样子:
以下是代码片段: reg [2:0] sta; case (sta) 3’b001: …; 3’b010: …; 3’b100: …; default: …; endcase |
这样,采用独热码似乎没有什么意义。而且因为采用独热码时,sta的比特数比采用格雷码时更多,复杂度反而还增加了。
另外,XST也可能没有把case语句转换为并行结构,而是有优先级的结构。
【Case Implementation Style】中有三个选项:【Full】、【Parallel】与【Full-Parallel】。其中,【Full】针对上述的第一点,向XST说明有些状态是不可能出现的,让XST不要考虑太多;【Parallel】让XST将case语句综合为并行的电路结构;【Full-Parallel】则是两者的结合。
对于状态机,这项设置的影响很大。器件为xc6vlx240t-1ff1156时,同样代码的8状态独热码状态机,缺省设置时占用11个寄存器、6个查找表,只能工作在575 MHz时钟频率下;改为【Full-Parallel】设置后,占用11个寄存器、3个查找表,可以工作在900 MHz时钟频率下。
【Xilinx Sepcific Options】
- case 语句未必被综合为并行电路。需要并行电路的话,需要设置【HDL Options】中的【Case Implementation Style】。
- 配合【Max Fanout】与【Register Duplication】来进行全局的寄存器复制,以减小扇出。

- 同步复位电路能够被综合为性能更好的基于硬核(块RAM、DSP等)的电路,异步复位电路则不能。
- 综合器能够将同步复位信号与其它信号放在一起优化,而异步复位信号只是复位信号。
以下是代码片段: reg [0:3] sta; always@(posedge clk) if (sta[0]) …; end else if (sta[1]) …; end else if (sta[2]) …; end else if (sta[3]) …; |
以下是代码片段: reg [0:3] sta; always@(posedge clk) case(1’b1) sta[0]: …; sta[1]: …; sta[2]: …; sta[3]: …; endcase |
- 能不用复位就不用复位;不能的话,采用同步复位。
- 尽量不要使用多层嵌套的条件判断语句。
- 一般情况下,需要用寄存器对模块的输入与输出进行缓存。
-
PART4 高速电路的设计方法在上一个部分中,说明了几种自己总结的利于设计出高速电路的代码风格。这里其实暗含着下面这样的场景:电路已经设计好了,而且已经很棒,只是需要用风格良好的 HDL 代码进行输入,以保证综合出的电路和设计的一样好。而在这一部分中,想讨论一下怎样设计高速电路。可以说,是进行 HDL 输入前的工作。最初的时候,是没有这个部分的。原因是这部分并非是自己总结的内容,更多还是从前辈们那儿吸取的经验。不过,少了这个部分,感觉有点缺乏完整性。毕竟 PART3 和 PART4 的内容,只是一些小技巧。而且我想了一下,将获取的知识再整理整理,也没有什么不好的。因此,这部分内容是我对 Steve Kilts 的《高级 FPGA 设计:结构、实现和优化》第 1 章 1.3 节(4 至 13 页)的总结。读过这本书的朋友可以跳过这个部分了。其中的例子大都是我自己归纳后想出的简单例子,可能还有些不妥之处,还请各位朋友指出。这里强烈推荐这本书。一般的 FPGA 书中,讲流程、接口的多,讲电路结构的感觉比较少,而这本书涉及的还挺多的;而比起 Parhi 的《VLSI 数字信号处理系统:设计与实现》这种比较纯粹的 IC 设计的专著(当然这本也很好),这本书又和 FPGA 有着非常紧密的联系。1 增加寄存器层次通过在组合逻辑中插入一级寄存器来优化关键路径。考虑下图所示的三输入加法器。此时电路的关键路径为两个加法器的门延时。
以下是代码片段:
always @ (posedge clk) begin
// 第一层
a_d <= a;
b_d <= b;
c_d <= c;
// 第二层
s <= m+c_d;
end
assign m = a_d+b_d;通过在两个加法器之间引入一级寄存器,能够将关键路径缩短为一个加法器的门延时。 当然,这会带来一个周期的时延,同时也引入了多余的寄存器,增加了电路面积。以下是代码片段:
always @ (posedge clk) begin
// 第一层
a_d <= a;
b_d <= b;
c_d <= c;
// 第二层
c_2d <= c_d;
m <= a_d+c_d;
// 第三层
s <= m+c_2d;
end2 并行结构与通常所说的通过增加并行路数从而提高吞吐量的并行架构不同,这里指的是通过重新组织组合逻辑的结构,来优化关键路径。考虑一个链状的四输入加法器电路。其关键路径为三个加法器的门延时。以下是代码片段:
assign m1 = a+b;
assign m2 = c+m1;
assign s = d+m2;如果采用树状结构,则关键路径仅为两个加法器的门延时。以下是代码片段:
assign m1 = a+b;
assign m2 = c+d;
assign s = m1+m2;3 去除电路中的优先级编码这点就是 PART3 中所说的“尽量不要使用多层嵌套的条件判断语句”。考虑一个四状态的独热码状态机,采用优先级编码的电路有四层组合逻辑。以下是代码片段:
reg [0:3] sta;
always@(posedge clk)
if (sta[0]) …;
end else if (sta[1]) …;
end else if (sta[2]) …;
end else if (sta[3]) …;而如果使用 case 语句,并在【综合选项】中设置好对应的项目(见 PART2),得到的非优先级编码电路则是并行结构,速度更快。以下是代码片段:
reg [0:3] sta;
always@(posedge clk)
case(1’b1)
sta[0]: …;
sta[1]: …;
sta[2]: …;
sta[3]: …;
endcase这里需要强调的是,如果电路的逻辑确实需要优先级,还应该使用嵌套的条件判断语句,而不该盲目采用无优先级的电路。4 寄存器平衡(重定时)电路的速度,是由关键路径决定的。因此在设计电路时,如果有一条路径的门延时特别长,其它路径的门延时再短,该电路的速度也仍然很慢。因此,设计电路时,可以通过改变寄存器的位置,使得各路径的门延时变得平均,以达到提高电路速度的目的。考虑下面的五输入加法器。可以看到,第一级寄存器和第二级寄存器之间的路径长度是一个加法器的门延时,而第二级寄存器和第三级寄存器之间的关键路径长度是三个加法器的门延时。因此,整个电路的关键路径长度是三个加法器的门延时。以下是代码片段:
always @ (posedge clk) begin
// 第一层
a_d <= a;
b_d <= b;
c_d <= c;
d_d <= d;
e_d <= e;
// 第二层
m1 <= a_d+b_d;
c_2d <= c_d;
d_2d <= d_d;
e_2d <= e_d;
// 第三层
s <= m4;
end
assign m2 = m1+c_2d;
assign m3 = m2+d_2d;
assign m4 = m3+e_2d;经过寄存器平衡后,输入输出之间的逻辑关系没有改变,但两条路径的长度都变为两个加法器的时延,整个电路的关键路径长度缩短为两个加法器的时延。以下是代码片段:
always @ (posedge clk) begin
// 第一层
a_d <= a;
b_d <= b;
c_d <= c;
d_d <= d;
e_d <= e;
// 第二层
m2 <= m1+c_d;
d_2d <= d_d;
e_2d <= e_d;
// 第三层
s <= m4;
end
assign m1 = a_d+b_d;
assign m3 = m2+d_2d;
assign m4 = m3+e_2d;在《高级 FPGA 设计:结构、实现和优化》中,还有一种“重新安排路径”的策略。对于这点,可能我的理解还不够深,感觉这一策略和上面第二种策略类似:都是通过重新组织组合逻辑的结构,以提升电路速度。此处就不对这种策略进行详细的说明了。总结- 最基本的策略:在组合逻辑中插入寄存器,通过引入时延与增大电路面积来提升电路速度。
- 组合电路设计策略:提升组合逻辑的并行度,缩短门延时。
- 条件判断电路设计策略:不需要优先级编码的话,采用无优先级的电路。
- 整体设计策略:移动寄存器位置,平衡各组合逻辑路径的门延时。
PART5 结语关于前一段时间优化电路速度工作的总结到这里就结束了。其实之前还打算通过使用位置约束,将各个模块约束在较小的范围内,以使 EDA 软件实现出更优化的电路。不过因为经验不足,实际操作时位置约束后性能反而更差。关于这点,还需要进一步的研究,也请朋友们指导一二。(全文完)