前面在讲到 “ 脉冲D触发器 ” 和 “ 边沿D触发器 ” 时,都遇到了不稳定状态(亚稳定状态)。前者是在正常工作中出现,我们选择了优化器件;后者是在建立保持时间不够时出现,我们选择了提供充足的时间,或多级级联(异步信号接入同步电路时会用到,下一篇文章会讲)。
今天让我们看看,同步电路内部,建立保持时间如何设计充足的吧。
1 同步电路内部
有设计基础的小可爱会说了,reg(寄存器[ˈredʒɪstər])区别于wire(线[ˈwaɪər]),是verilog语句中的寄存器类型,咋变成D触发器啦。首先reg和D触发器一样,具有记忆存储功能;其次,reg没办法在组合逻辑中作为逻辑输出,而是像D触发器一样,在时序逻辑中经边沿锁存。所以我们可以认为reg就是D触发器。

图1是由两个D触发器(REG1,REG2)(后文用D触发器指代上升沿D触发器)级联组成的电路。在默认REG1.D的建立时间充足的情况下,我们关注的是时钟、数据从源出发,流经REG1、REG2的过程及时延变化,最终分析REG2的输入是否满足建立、保持时间。
“ Tclk1 ” :时钟信号CLK抵达REG1花费的时间,线路延时。
“ Tclk2 ” :类似Tclk1,是CLK抵达REG2的延时。
“ Tdata ” :REG1的输出Q抵达REG2的输入D花费的时间,线路延时(如果这里存在功能模块,还需要加上模块延时)。
“ uTco ” :数据在D触发器内花费时间(可以理解为最小保持时间)。
“ uTsu ” :最小建立时间。
“ uTh ” :最小保持时间。
2 时序图分析

时序分析结果如图2。默认左边是时间轴原点,信号随时间推移,从左往右刷新出来。
“ CLK ” :CLK输入脚的时钟源。
“ 数据抵达时间 ” :时钟上升沿消耗 + Tclk1 + uTco + Tdata。(即REG1.D到达REG2.D的时间)
“ 时钟抵达时间 ” :时钟上升沿消耗 + Tclk2。(即 “ CLK ” 到达REG2.CLK的时间)
3 公式推导
如果REG1.D是与CLK同步的数据(即由CLK边沿触发的数据,类似REG2.D的情形):
“ 保持时间 ” = “ 数据抵达时间 ” – “ 时钟抵达时间 ”
Th = Tclk1 + uTco + Tdata – Tclk2
“ 保持富余时间 ” = “ 保持时间 ” – “ 最小保持时间 ”
Hold Slack = Th – uTh
“ 建立时间 ” = “ CLK周期 ” – “ 保持时间 ”
Tsu = T – Th
“ 建立富余时间 ” = “ 建立时间 ” – “ 最小建立时间 ”
Setup Slack = Tsu – uTsu
4 TimeQuest Timing Analyzer (探索[kwest] 分析器[‘ænəˌlaɪzə])

“ Launch Clock ” (启动[lɔːntʃ]):“ CLK ” 时钟前一个上升沿,经过Tclk1时延后,给REG1提供锁存Data1。
“ Latch Clock ” (锁存[lætʃ]):“ CLK ” 时钟后一个上升沿, 经过Tclk2时延后,给REG2提供锁存Data1 。(该边沿在REG1中锁存Data2)
“ Data Arrival ” (到达[əˈraɪvl]):REG1.D经过 “ 数据抵达时间 ” 后,抵达REG2.D的数据。
“ Clock Delay ” :上面那个是Tclk1,下面那个是Tclk2。
“ Data Delay ” : uTco + Tdata 。
“ Slack ” (富余的部分[slæk]): “ 建立富余时间 ”。( “ 保持时间 ” 和 “ 保持富余时间 ” 是固定的,所以没必要单独拿出来分析)
“ uTsu ” :最小建立时间,程序默认的是0.034us,这种数量级可以类推到最小保持时间、富余量是否足够等上面。
注意,这次模拟中Tclk2 < Tclk1,这是有可能的,取决于芯片内部哪一条线里源更近,和图1中谁长没关系。
感兴趣的可以看看数据路径说明,不感兴趣的可以跳过。

5 总结归纳
传输过程中会诞生出一个 “ 保持时间 ” ,其大小往往比 “ 最小保持时间 ” 要长。所以我们在设计的时候不会考虑 “ 最小保持时间 ” 是否满足,而是会去考虑 “ CLK周期 ” 是否能放下 “ 保持时间 ” 。
当然也有例外的情况,如 Th = Tclk1 + uTco + Tdata – Tclk2 < uTh,这种情况就需要我们优化其中的参数,使其满足Th > uTh。
“ 最小建立时间 ” 和 “ 最小保持时间 ” 不同,需要我们额外增加到 “ CLK周期 ” 中,并提供一定的富余量,即 “ 建立富余时间 ” ,即 “ Slack ” 。使得 “ CLK周期 ” = “ 保持时间 ” + “ 建立时间 ” 。
5.1 时间不充足如何解决
从原理上讲,有两种方式可以解决时间不充足的问题。
其一:增加该同步电路的 “ CLK周期 ” ,周期变长了, “ 建立富余时间 ” 就更充足了。
其二:在不想增加周期的情况下,减少 “ 保持时间 ” ,也可以腾出一些时间作为 “ 建立富余时间 ” 。如将 “ Tdata ” 中的组合逻辑,通过一个或多个D触发器分割,实现流水线作业等。这种处理需要结合产生的延时,考虑是否划得来。
5.2 某同步电路最大时钟频率
“ 最大的时钟频率 ” = 1 / “ 最小的CLK周期 ” = 1 / ( “ 保持时间 ” + “ 最小建立时间 ” )
f_max = 1 / (Th + uTsu) = 1 / ( Tclk1 + uTco + Tdata – Tclk2 + uTsu)
“ 数据的最大频率 ” = “ 最大的时钟频率 ” (由于是同步电路,数据和时钟的最大频率是一致的)
注意,实际选取时钟频率的时候,首先要满足芯片最高时钟(如最高250Mhz)等要求,其次要加入一定的 “ 建立富余时间 ” ,不能过于冒险。如,在芯片允许的情况下, “ CLK周期 ” 可以取两倍的 “ 保持时间 ” 。
5.3 流水线设计
流水线设计可以加快系统的工作速度,增大吞吐量。让我们简单分析一下:


将图5中CL0(组合逻辑),分拆成CL1、CL2等多个部分,并通过D触发器将其分隔开,就成了我们所说的流水线设计。如图6。
在图5中,输入IN依次进入CL0,且下一个输入IN要等CL0处理完成才能进入。
而图6中,输入IN依次进入CL1,、CL2,且下一个输入IN只用等到CL1处理完成就可进入。这样的设计可以提高工作速度、增加吞吐量,但相应的增加了输出的延时,有得必有失。
理解了原理,还可以比较下 “ 最大的时钟频率 ” 的变化情况,注意Tdata不仅仅是线路延时,也包含组合逻辑电路消耗的时间。这里就不计算了,感兴趣的小可爱可以自行推导。
6 结束语
同步电路内部的建立保持时间问题解决了,万事大吉,但是异步数据接入同步电路,压根没法保证建立保持时间,该怎么办呢?一般我们会用到两到三个D触发器级联来解决。
这篇的分析有点多,头都大了,多级级联就留在下一篇吧。
最后附上引用的文章:
- Quartus_II_Design_Series_Timing Analysis.ppt ([‘kwɔtəs] 系列[ˈsɪriːz] 分析[əˈnæləsɪs])(这是ALTERA的时序分析ppt,感兴趣的小可爱可以自己找来看看)