本文是《我不是只懂verilog语法》系列文章的第一篇,今后我会根据我的开发经历,介绍一些FPGA优化设计的经验。作者水平有限,难免有考虑不周全的地方,希望大家可以指出。另外,如果有人希望转载,我非常欢迎,只是希望注明出处:EDN博客——山寨战胜一切;作者:hjzwy
我们常常听人说,要写出风格好的verilog代码。但是,究竟什么是风格好的verilog代码呢?当我认真思考这个问题的时候,我才发现,原来我只是懂verilog的语法而已,而真正的FPGA设计,还没有入门。
在写verilog的时候,我们常常会时序逻辑中加入异步复位,以期在复位时,可以将各寄存器的值复位到我们所期望的初始状态。这看似简单的代码中,其实也有很多学问。比如说这样一段代码:
always@(negedge Vld_LinkClk)
begin
if(!TokenValid)
begin
DataRev8_Cnt <= 2'b00;
DataRev8_NegE[0] <= 8'h0;
DataRev8_NegE[1] <= 8'h0;
DataRev8_NegE[2] <= 8'h0;
DataRev8_NegE[3] <= 8'h0;
end
else
begin
DataRev8_Cnt <= DataRev8_Cnt + 1'b1;
DataRev8_NegE[DataRev8_Cnt] <= LinkData;
end
end
本段代码截选自本人一个项目中,并作了少量修改。可以看到TokenValid是复位信号。这段代码的功能是TokenValid为低时将这些寄存器清零,TokenValid为高时每个时钟周期DataRev8_Cnt自增,并将LinkData赋给DataRev8_NegE[DataRev8_Cnt]。
细心的看官可能会发现,在本always块中,每感事件列表中只有Vld_LinkClk,而没有TokenValid。所以,此时TokenValid只能称为同步复位信号,因为只有在Vld_LinkClk下降沿时,才有可能发生复位。
这样做好不好呢?我觉得不好,这就要从Altera的FPGA的LE结构说起了。
从cyclone 2手册上抄了一个LE的结构图下来:
可以发现,LE主要由1个触发器和1个四输入查找表组成,外加一些逻辑。
由LE的内部结构可以看出,它含有一个异步清零端和一个异步置位端。当然,从图上还可以看出,它还有同步清零和置位的逻辑(Synchronous Load and Clear Logic),这部分逻辑是组合逻辑电路。
好,介绍清楚了LE的内部结构,下面看一下上面的代码所综合出来的RTL电路,我只截选RTL电路中DataRev8_Cnt的部分:
可以看出,复位清零的逻辑是通过一个双路选通器MUX21来完成的,当TokenValid为0时,DATAB选通输出至OUT0。
我们知道,FPGA设计中应该尽量减少组合逻辑,因为它们加了延时,并且这个延时是EDA工具难以预知的,如上图中,DATAA到OUT0这段存在延时,限制了Vld_LinkClk的提升(如果不理解为什么延时会限制Vld_LinkClk的提升,可以看一下FPGA中时序分析的基础)。
此时的时序分析结果如下图:
可以看出,Vld_LinkClk最快可以跑到165.78M,本设计的另一个时钟Sys_Clk最快可以跑到161.92M。
好,现在将同步复位改为异步复位,即把敏感事件列表中加入TokenValid:
always@(negedge Vld_LinkClk or negedge TokenValid)
这样修改后,RTL电路中DataRev8_Cnt的部分如下:
可以看出,双路选通器MUX21不见了,TokenValid直接接到了触发器的异步复位端上,完成异步复位功能。这样在数据线上就没有了组合逻辑部分,可以在一定程序上提高时钟频率上限。
此时的时序分析结果如下图:
可以看出,Vld_LinkClk最快可以跑到167M,本设计的另一个时钟Sys_Clk最快可以跑到226.96M。
Vld_LinkClk提高了一点点,而另一个时钟Sys_Clk提高了非常多。
另外,在异步复位中,复位时给寄存器所赋的值也是有讲究的。例如,将DataRev8_NegE[0]初始值赋为0xAA。
if(!TokenValid)
begin
DataRev8_Cnt <= 2'b00;
DataRev8_NegE[0] <= 8'hAA;
DataRev8_NegE[1] <= 8'h0;
DataRev8_NegE[2] <= 8'h0;
DataRev8_NegE[3] <= 8'h0;
end
此时的RTL中,复位信号连到了DataRev8_NegE[0]中初始化为0的位所对应的寄存器的清零端,初始化为1的位所对应的寄存器的置位端。
综合后的结果中,Vld_LinkClk最快可以跑到136M,另一个时钟Sys_Clk最快可以跑到226.91M。
可见,这样做会影响Vld_LinkClk。这其中的原因我还不知道,希望有人能够给出答案。
在进行初始化时,尽量初始化为0,异步复位。这样可以达到最优化的结果。
如果在复位中,不是对寄存器赋值,而是对RAM块或其它模块赋值,就需要考虑这个模块是否具有异步复位或置位端,如果有,OK。如果没有,那么就会生成许多组合逻辑电路,完成复位的功能,这将会影响逻辑最高可以跑的速度。
所以,加什么复位,加不加复位,一定要看清楚底层的结构,这将极大地改善设计的性能。这对FPGA设计者提出了一个较PC软件开发者、嵌入式开发者更为高的要求。
用户617679 2010-9-16 22:17
用户1359795 2010-9-14 23:18
用户222522 2010-9-14 15:55
用户1584993 2010-9-8 17:55
用户1039119 2010-9-8 14:11