逻辑简单的小程序模块时钟频率可达到上百兆甚至几百兆,而作为复杂系统的全局时钟通常只在几十兆,一般不超过100M。当然,对于一个小型CPLD程序,几兆或千赫兹时钟即能完成整个功能的情况就另当别论了。一般来说,把系统时钟设计在50M到100M这个范围是最佳选择。这个范围的时钟频率远远高于通常的低速信号,这在处理低速信号时有很大的自由。同时这个范围的时钟用16位并行总线即可以接管上吉速率的串行数据。这就是同步系统设计准则1,系统时钟选定在50M~100M范围。这是根据当前FPGA工艺水平和通常的逻辑功能确定的。接下来的问题是如何把不同接口同步到系统时钟上来。对于低于系统时钟一半以下的低速信号,建议采用的办法是把接口的时钟信号转化为系统时钟的使能信号,用使能信号来控制接口数据的读写节拍。而对于接近系统时钟的中速信号,先按照其时钟进行独立的接口功能处理,达到字(位宽随具体情况而定)同步后再转交系统时钟,同样以使能方式控制节拍。交给接口输出的数据,同样是在系统时钟下处理接近最后一步,再交由接口时钟跳变沿输出。对于高于系统时钟的高速信号,有时要使用FPGA专有的接口资源完成串并变换,甚至需要进一步扩大位宽来降速再转交系统时钟。由系统到接口的过程则相反。具体剖析这些方法之前,先强调几条同步设计准则,这些准则在一些FPGA新手指南中也会提到,本文结合个人的理解增加一些具体分析。
准则2,不依赖复位。这是从系统的健壮性角度提出的要求,意思是不能依赖按下一次复位键后系统才能正常工作。例如,对于一个4比特的伪随机序列prb(0 to 3)其生成多项式为1 + x + x^4,其电路为:在时钟进程里prb <= (prb(0) xor prb(3)) & prb(0 to 2);我们知道这个电路在prb不为全0时工作正常,序列周期长度为15。而当prb=0后,就停留在0这个状态了。依赖复位的做法是,按下复位键给prb赋一个非全0值。且不说复位键带来多余的操作,设想系统正在模15正常工作,因意外干扰是prb值变成0,那么干扰消失后系统确停留在死循环状态了。如果我们把电路写成prb <=( (prb(0) xor prb(3)) or prb=0) & prb(0 to 2);那么此时的电路就变成了自启动电路,不再存在上述死等顾虑。这种不依赖复位的自启动电路设计,可以从意外故障中自动恢复,在非全状态的状态机、移位计数器等电路中均应考虑。另外,这里以及下文说的复位,并不一定是指赋全0值,而是泛指装载初始值。
准则3,如非必须不使用异步复位。异步复位究竟会带来什么问题呢?例,一个计数器cn在rst为高则赋0值,否则在时钟上升沿cn <= cn + 9。这个电路在rst为0后,cn预期的计数结果是0,9,18,27,...按9的倍数增长直到溢出处理。假设cn是10位计数器cn[9..0],也即cn电路上是由十个触发器cn[9], cn[8], .. , cn[0]构成,复位信号rst分别引线到这10个触发器。那么这10条引线带来的延迟不可能绝对相等。设想cn[3]处延时较小,则存在cn[3]处观察到rst在时钟沿之前已由1跳变到0而在cn[0]处时钟沿之前仍为1在时钟沿之后才跳变到0的时序。此时,cn的计数结果是0, 8, 17 ,26, ...。反之,如果cn[0]出延时小,则有可能是0, 1, 10, 19, 28, ...。当然,若在电路设计时已考虑到这一极限情况,或者rst与时钟相位有确定的关系,使用异步复位也无妨。
准则4,异源信号不在多处逻辑使用。例,电路需要判断一个异源信号A由0变为1。也就是说,在前一个时钟上沿看到的A是0,当前一个时钟看到的是1。那么很容易想到的逻辑就是if rising_edge(clock) then S <= A; end if; S为前一个时钟读到的值,ifA=1 and S=0 then ...这一电路目的是实现A每次由0变1后做一次省略部分的后续处理。此电路在两处使用了异源信号A,分别是读取A和判断A两处。同样设想A在时钟上沿附近变化,读取A电路对A的延时较小,读取电路的当前周期S=0下一周期S=1,而判断电路对A的延时较大导致识别A同样是当前周期A=0,下一周期A=1。这样A=1 and S=0的条件电路就遗漏了一次A由0到1的跳变事件。反之,如果读取电路对A的延时较大而判断电路延时较小,又会把一次跳变事件判断为两次连续的跳变事件。这就导致了逻辑出错。正确的做法是,再延时一个周期处理if rising_edge(clock) then S0 <= A; S1 <= S0; endif; 然后判断电路相应修改为if S0=1 and S1=0 then ...。此时异源信号A只在一处使用。同准则3一样,举例为简单逻辑,对于复杂逻辑不同电路相对延时差增大,发生这种错误的可能进一步增大。还有一种隐性的异源信号在多处逻辑使用的情况,例如对信号A积分,当A为1时cn增计数,A为0时减计数。如果写成if A=1 then cn <= cn + 1; else cn <= cn-1; end if;,由于cn代表着多个触发器,就隐含着在多处逻辑使用了信号A。
准则5,慎用双时钟异步读写RAM。当出现跨时钟域数据交互的时候,有些FPGA程序设计者用IP核生成异步RAM,然后在不同的时钟域内处理,不同时钟的问题放心地交给IP核生成的逻辑就万事大吉了。当然,异步RAM本身没有问题。但是对RAM的读写寻址,判断RAM空满或已用RAM空间,同样要选定在同一个时钟下完成。读写地址是并行数据,每个比特跳变有先后,不作同步处理,直接在一个时钟下判断是危险的行为。这一点,我们在以后的具体事例中再作讨论。
|