注意:当使用多级非门的时候综合器往往会将其优化掉,因为综合器会认为一个信号非两次还是它自己。
需要说明的是在FPGA/CPLD内部结构是一种标准的宏单元,下图是Xilinx公司的Spartans II系列器件的一个标准宏单元。虽然不同的厂家的芯片宏单元的结构不同,但概括而言都是由一些组合逻辑外加一或二个触发器而构成。在实际应用中,当一个模块内的组合逻辑被使用了那么与其对应的触发器也就不能用了;同样如果触发器单元被用了那么组合逻辑单元也就废了。这就是有时候(特别是使用CPLD)虽然设计使用的资源并不多但布局布线器却报告资源不够使用的原因。
现面的一个例子是前一段时间我在公司遇到的一个设计。设计使用Altera公司的EPM7256型号的CPLD。该设计实际使用的寄存器资源只有109个,占整个器件资源的42%。可是该设计使用了如下图所示的延时方法来做处理器接口的时序:
现面的一个例子是前一段时间我在公司遇到的一个设计。设计使用Altera公司的EPM7256型号的CPLD。该设计实际使用的寄存器资源只有109个,占整个器件资源的42%。可是该设计使用了如下图所示的延时方法来做处理器接口的时序:
在该电路的设计中使用了大量的LCELL来产生100多纳秒的延时,这样做的后果是虽然整个电路的触发器资源只使用了42%,可是用MaxplusII进行布局布线已经不能够通过了。而且我怀疑经过这么多逻辑的延时后所产生的信号还能保持原来的性能不。
当需要对某一信号作一段延时时,初学者往往在此信号后串接一些非门或其它门电路,此方法在分离电路中是可行的。但在FPGA中,开发软件在综合设计时会将这些门当作冗余逻辑去掉,达不到延时的效果。用ALTERA公司的MaxplusII开发FPGA时,可以通过插入一些LCELL原语来产生一定的延时,但这样形成的延时在FPGA芯片中并不稳定,会随温度等外部环境的改变而改变,因此并不提倡这样做。在此,可以用高频时钟来驱动一移位寄存器,待延时信号作数据输入,按所需延时正确设置移位寄存器的级数,移位寄存器的输出即为延时后的信号。此方法产生的延时信号与原信号比有误差,误差大小由高频时钟的周期来决定。对于数据信号的延时,在输出端用数据时钟对延时后信号重新采样,就可以消除误差。
对于这样大的延时我建议的实现方法是采用时钟锁存来产生延时的方法,我们知道当一个信号用时钟锁存一次,将会占用一个触发器资源,信号会向后推移一个时钟周期;该同事的设计里CPLD芯片正好连接有32MHz的时钟,那么每用时钟锁存一次ssp信号就会推移31ns,这样只需多使用3个触发器资源就可以达到目的了。电路图和仿真波形如下图所示:当然这样做对原来信号高低电平的宽度会稍有改变,但只要是在与其接口的芯片的容许范围之内就不会影响到功能的实现。
用于延时的电路图
上图仿真波形
2.3 如何提高系统的运行速度
同步电路的速度是指同步时钟的速度。同步时钟愈快,电路处理数据的时间间隔越短,电路在单位时间处理的数据量就愈大.
我们先来看一看同步电路中数据传递的一个基本模型,如下图:
(Tco是触发器时钟到数据输出的延时;Tdelay是组合逻辑的延时;Tsetup是触发器的建立时间)
假设数据已经被时钟的上升沿打入D触发器,那么数据到达第一个触发器的Q端需要Tco,再经过组合逻辑的延时Tdelay到达的第二个触发器的D端,要想时钟能在第二个触发器再次被稳定的锁入触发器,则时钟的延迟不能晚于Tco+Tdelay+Tsetup,(我们可以回顾一下前面讲过的建立和保持时间的概念,就可以理解为什么公式最后要加上一个Tdelay) 由以上分析可知:最小时钟周期:T=Tco+Tdelay+Tsetup 最快时钟频率 F= 1/T PLD开发软件也正是通过这个公式来计算系统运行速度Fmax。
注:在这个逻辑图中有个参数:Tpd ,即时钟的延时参数,我们在刚才做时间分析的时候,没有提这个参数,(如果使用PLD的全局时钟型号,Tpd可以为0,如果是普通时钟,则不为0)。所以如果考虑到时钟的延时,精确的公式应该是T=Tco+Tdelay+Tsetup-Tpd。当然以上全部分析的都是器件内部的运行速度,如果考虑芯片I/O管脚延时对系统速度的影响,那么还需要加一些修正。
由于Tco、Tsetup是由具体的器件和工艺决定的,我们设计电路时只可以改变Tdelay。所以缩短触发器间组合逻辑的延时是提高同步电路速度的关键。由于一般同步电路都不止一级锁存(如图3),而要使电路稳定工作,时钟周期必须满足最大延时要求,缩短最长延时路径,才可提高电路的工作频率。
如图2所示:我们可以将较大的组合逻辑分解为较小的几块,中间插入触发器,这样可以提高电路的工作频率。这也是所谓“流水线”(pipelining)技术的基本原理。
对于图3的上半部分,它时钟频率受制于第二个较大的组合逻辑的延时,通过适当的方法平均分配组合逻辑,可以避免在两个触发器之间出现过大的延时,消除速度瓶颈。
FPGA/CPLD开发软件中也有一些参数设置,通过修改这些设置,可以提高编译/布局布线后系统速度,但是根据经验这种速度的提高是很有限的,假如按照要求我们需要设计一个可以工作到50MHz的系统,实际布局布线器报告出来的Fmax只有40MHz,此时如果我们使用布局布线器的设置选项最多可以提高到45MHz,这还是运气比较好的情况。而且你必须了解这些选项的含义、使用背景等。
其实在一个设计里影响速度的瓶颈经常只会有几条,我们将延时最大的路径称作关键路径。当设计的运行速度不符合系统设计要求的时候我们可以首先找到不能满足要求的关键路径,按照上述的方法将关键路径上的组合逻辑拆分成多个中间用触发器隔开,这样很容易就可以从根本上提升系统的运行速度了。
有的设计在设计开始就知道那部分电路会产生比较大的组合逻辑,导致速度瓶颈的产生,那么就应该在开始就想好解决办法。比如现在设计需要产生一个32位的加法器,并且要求能够工作在50MHz。根据经验直接用32位加法器肯定是达不到50MHz的要求的,这时我们可以将其分成3个12位计数器来操作,后面的计数器只要将前面计数器结果的高位(进位位)相加就可以了。
下面是原来在宽带接入服务器设计中的流量统计单元中的32位加法器的描述:
----------------------------------------------------------
---- flow count element
----------------------------------------------------------
-----temporary computing 12 bits adder
process(Count_0_en,count_buffer,Len,Carry_0_0,Carry_0_1)
begin
case Count_0_en is
---1st Step addition (10 downto 0) + (10 downto 0)
when "001" => add_12_a_0 <= ('0' & count_buffer(0)(10 downto 0));
add_12_b_0 <= ('0' & Len(10 downto 0));
---2nd Step addition (21 downto 11) + Carry_0_0
when "010" => add_12_a_0 <= ('0' & count_buffer(0)(21 downto 11));
add_12_b_0 <= ("00000000000" & Carry_0_0);
---3rd Step addition (31 downto 22) + Carry_0_1
when "100" => add_12_a_0 <= ("00" & count_buffer(0)(31 downto 22));
add_12_b_0 <= ("00000000000" & Carry_0_1);
when others => add_12_a_0 <=(others=>’X’);
add_12_b_0 <=(others=>’X’);
end case;
end process;
------12 bits adder
add_12_result_0 <= add_12_a_0 + add_12_b_0;
------Bytes Count
process(RST,CLK_25MHz,IO,OE_bar,data_sel,Count_0_en)
begin
if(RST = '1')then -----system Reset
count_buffer(0) <= (others => '0');
Carry_0_0 <= '0';
Carry_0_1 <= '0';
Carry_0_2 <= '0';
elsif(CLK_25MHz'event and CLK_25MHz = '0')then
if(OE_bar = '0' and data_sel = '0')then
count_buffer(0) <= IO;
Carry_0_2 <= '0';
else
case Count_0_en is
---1st Step addition (10 downto 0) + (10 downto 0)
when "001" => count_buffer(0)(10 downto 0) <= add_12_result_0(10 downto 0);
Carry_0_0 <= add_12_result_0(11);--first step carry
---2nd Step addition (21 downto 11) + Carry_0_0
when "010" => count_buffer(0)(21 downto 11) <= add_12_result_0(10 downto 0);
Carry_0_1 <= add_12_result_0(11);--Second step carry
---3rd Step addition (31 downto 22) + Carry_0_1
when "100" => count_buffer(0)(31 downto 22) <= add_12_result_0(9 downto 0);
Carry_0_2 <= add_12_result_0(10);--Third step carry
when others => Carry_0_2 <= '0';
end case;
end if;
end if;
end process;
评论
查看更多